diff --git a/internal/controller/eventing/controller.go b/internal/controller/eventing/controller.go index 06f9cb6c..b87908e1 100644 --- a/internal/controller/eventing/controller.go +++ b/internal/controller/eventing/controller.go @@ -20,10 +20,7 @@ import ( "context" "errors" "fmt" - - "github.com/kyma-project/eventing-manager/pkg/watcher" - - "k8s.io/client-go/dynamic" + "strings" "go.uber.org/zap" v1 "k8s.io/api/apps/v1" @@ -34,8 +31,10 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/dynamic" "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/event" @@ -50,8 +49,10 @@ import ( "github.com/kyma-project/eventing-manager/pkg/eventing" "github.com/kyma-project/eventing-manager/pkg/k8s" "github.com/kyma-project/eventing-manager/pkg/logger" + "github.com/kyma-project/eventing-manager/pkg/object" "github.com/kyma-project/eventing-manager/pkg/subscriptionmanager" "github.com/kyma-project/eventing-manager/pkg/subscriptionmanager/manager" + "github.com/kyma-project/eventing-manager/pkg/watcher" ) const ( @@ -169,36 +170,36 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu } // copy the object, so we don't modify the source object - eventing := currentEventing.DeepCopy() + eventingCR := currentEventing.DeepCopy() - // watch cluster-scoped resources, such as clusterrole and clusterrolebinding. + // watch cluster-scoped resources, such as ClusterRole and ClusterRoleBinding. if !r.clusterScopedResourcesWatched { - if err := r.watchResource(&rbacv1.ClusterRole{}, eventing); err != nil { + if err := r.watchResource(&rbacv1.ClusterRole{}, eventingCR); err != nil { return ctrl.Result{}, err } - if err := r.watchResource(&rbacv1.ClusterRoleBinding{}, eventing); err != nil { + if err := r.watchResource(&rbacv1.ClusterRoleBinding{}, eventingCR); err != nil { return ctrl.Result{}, err } r.clusterScopedResourcesWatched = true } // logger with eventing details - log := r.loggerWithEventing(eventing) + log := r.loggerWithEventing(eventingCR) // check if eventing is in deletion state - if !eventing.DeletionTimestamp.IsZero() { - return r.handleEventingDeletion(ctx, eventing, log) + if !eventingCR.DeletionTimestamp.IsZero() { + return r.handleEventingDeletion(ctx, eventingCR, log) } // check if the Eventing CR is allowed to be created. if r.allowedEventingCR != nil { - if result, err := r.handleEventingCRAllowedCheck(ctx, eventing, log); !result || err != nil { + if result, err := r.handleEventingCRAllowedCheck(ctx, eventingCR, log); !result || err != nil { return ctrl.Result{}, err } } // handle reconciliation - return r.handleEventingReconcile(ctx, eventing, log) + return r.handleEventingReconcile(ctx, eventingCR, log) } // handleEventingCRAllowedCheck checks if Eventing CR is allowed to be created or not. @@ -221,6 +222,37 @@ func (r *Reconciler) handleEventingCRAllowedCheck(ctx context.Context, eventing return false, r.syncEventingStatus(ctx, eventing, log) } +func (r *Reconciler) logEventForResource(name, namespace, resourceType, eventType string, enqueue bool) { + r.namedLogger(). + With("Name", name). + With("Namespace", namespace). + With("ResourceType", resourceType). + With("EventType", eventType). + With("Enqueue", enqueue). + Debug("Event received") +} + +func (r *Reconciler) SkipEnqueueOnCreate() func(event.CreateEvent) bool { + return func(e event.CreateEvent) bool { + // always skip enqueue + return false + } +} + +func (r *Reconciler) SkipEnqueueOnUpdateAfterSemanticCompare(resourceType, nameFilter string) func(event.UpdateEvent) bool { + return func(e event.UpdateEvent) bool { + // skip enqueue if the resource is not related to Eventing + if !strings.Contains(e.ObjectOld.GetName(), nameFilter) { + return false + } + + // enqueue only if the resource changed + enqueue := !object.Semantic.DeepEqual(e.ObjectOld, e.ObjectNew) + r.logEventForResource(e.ObjectOld.GetName(), e.ObjectOld.GetNamespace(), resourceType, "Update", enqueue) + return enqueue + } +} + // SetupWithManager sets up the controller with the Manager. func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { r.ctrlManager = mgr @@ -228,10 +260,38 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { var err error r.controller, err = ctrl.NewControllerManagedBy(mgr). For(&eventingv1alpha1.Eventing{}). - Owns(&v1.Deployment{}). - Owns(&corev1.Service{}). - Owns(&corev1.ServiceAccount{}). - Owns(&autoscalingv2.HorizontalPodAutoscaler{}). + Owns(&v1.Deployment{}, + builder.WithPredicates( + predicate.Funcs{ + CreateFunc: r.SkipEnqueueOnCreate(), + UpdateFunc: r.SkipEnqueueOnUpdateAfterSemanticCompare("Deployment", "eventing"), + }, + ), + ). + Owns(&corev1.Service{}, + builder.WithPredicates( + predicate.Funcs{ + CreateFunc: r.SkipEnqueueOnCreate(), + UpdateFunc: r.SkipEnqueueOnUpdateAfterSemanticCompare("Service", "eventing"), + }, + ), + ). + Owns(&corev1.ServiceAccount{}, + builder.WithPredicates( + predicate.Funcs{ + CreateFunc: r.SkipEnqueueOnCreate(), + UpdateFunc: r.SkipEnqueueOnUpdateAfterSemanticCompare("SA", "eventing"), + }, + ), + ). + Owns(&autoscalingv2.HorizontalPodAutoscaler{}, + builder.WithPredicates( + predicate.Funcs{ + CreateFunc: r.SkipEnqueueOnCreate(), + UpdateFunc: r.SkipEnqueueOnUpdateAfterSemanticCompare("HPA", "eventing"), + }, + ), + ). WithOptions(controller.Options{ MaxConcurrentReconciles: 0, }). @@ -255,9 +315,8 @@ func (r *Reconciler) watchResource(kind client.Object, eventing *eventingv1alpha predicate.ResourceVersionChangedPredicate{}, predicate.Funcs{ // don't reconcile for create events - CreateFunc: func(e event.CreateEvent) bool { - return false - }, + CreateFunc: r.SkipEnqueueOnCreate(), + UpdateFunc: r.SkipEnqueueOnUpdateAfterSemanticCompare("CR/CRB", "eventing"), }, ) return err diff --git a/internal/controller/eventing/eventmesh.go b/internal/controller/eventing/eventmesh.go index f4662cec..fefa139e 100644 --- a/internal/controller/eventing/eventmesh.go +++ b/internal/controller/eventing/eventmesh.go @@ -7,17 +7,18 @@ import ( "fmt" "os" - "github.com/kyma-project/eventing-manager/api/v1alpha1" - "github.com/kyma-project/eventing-manager/pkg/env" - "github.com/kyma-project/eventing-manager/pkg/eventing" - subscriptionmanager "github.com/kyma-project/eventing-manager/pkg/subscriptionmanager/manager" - "github.com/kyma-project/eventing-manager/pkg/utils" "github.com/pkg/errors" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + + "github.com/kyma-project/eventing-manager/api/v1alpha1" + "github.com/kyma-project/eventing-manager/pkg/env" + "github.com/kyma-project/eventing-manager/pkg/eventing" + subscriptionmanager "github.com/kyma-project/eventing-manager/pkg/subscriptionmanager/manager" + "github.com/kyma-project/eventing-manager/pkg/utils" ) const ( diff --git a/internal/controller/eventing/eventmesh_test.go b/internal/controller/eventing/eventmesh_test.go index c24b47f2..ac890e0f 100644 --- a/internal/controller/eventing/eventmesh_test.go +++ b/internal/controller/eventing/eventmesh_test.go @@ -11,12 +11,6 @@ import ( "github.com/kyma-project/eventing-manager/pkg/k8s" k8smocks "github.com/kyma-project/eventing-manager/pkg/k8s/mocks" - "github.com/kyma-project/eventing-manager/pkg/env" - managermocks "github.com/kyma-project/eventing-manager/pkg/eventing/mocks" - "github.com/kyma-project/eventing-manager/pkg/logger" - submanagermocks "github.com/kyma-project/eventing-manager/pkg/subscriptionmanager/manager/mocks" - subscriptionmanagermocks "github.com/kyma-project/eventing-manager/pkg/subscriptionmanager/mocks" - "github.com/kyma-project/eventing-manager/test/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -24,6 +18,13 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/kyma-project/eventing-manager/pkg/env" + managermocks "github.com/kyma-project/eventing-manager/pkg/eventing/mocks" + "github.com/kyma-project/eventing-manager/pkg/logger" + submanagermocks "github.com/kyma-project/eventing-manager/pkg/subscriptionmanager/manager/mocks" + subscriptionmanagermocks "github.com/kyma-project/eventing-manager/pkg/subscriptionmanager/mocks" + "github.com/kyma-project/eventing-manager/test/utils" ) const ( @@ -918,7 +919,7 @@ func Test_SyncPublisherProxySecret(t *testing.T) { }, // patchApply error { - name: "pathApply should fail", + name: "patchApply should fail", givenSecret: utils.NewEventMeshSecret("valid", "test-namespace"), mockKubeClient: func() *k8smocks.Client { kubeClient := new(k8smocks.Client) diff --git a/internal/controller/eventing/integrationtests/controller/integration_test.go b/internal/controller/eventing/integrationtests/controller/integration_test.go index 73a39795..21eb17dc 100644 --- a/internal/controller/eventing/integrationtests/controller/integration_test.go +++ b/internal/controller/eventing/integrationtests/controller/integration_test.go @@ -7,25 +7,25 @@ import ( "os" "testing" - "github.com/kyma-project/eventing-manager/pkg/k8s" + "github.com/onsi/gomega" + gomegatypes "github.com/onsi/gomega/types" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/apps/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + eventinv1alpha2 "github.com/kyma-project/kyma/components/eventing-controller/api/v1alpha2" + natsv1alpha1 "github.com/kyma-project/nats-manager/api/v1alpha1" + natstestutils "github.com/kyma-project/nats-manager/testutils" + eventingv1alpha1 "github.com/kyma-project/eventing-manager/api/v1alpha1" eventingcontroller "github.com/kyma-project/eventing-manager/internal/controller/eventing" "github.com/kyma-project/eventing-manager/pkg/eventing" + "github.com/kyma-project/eventing-manager/pkg/k8s" "github.com/kyma-project/eventing-manager/test/matchers" "github.com/kyma-project/eventing-manager/test/utils" testutils "github.com/kyma-project/eventing-manager/test/utils/integration" - natsv1alpha1 "github.com/kyma-project/nats-manager/api/v1alpha1" - natstestutils "github.com/kyma-project/nats-manager/testutils" - "github.com/onsi/gomega" - gomegatypes "github.com/onsi/gomega/types" - "github.com/stretchr/testify/require" - - eventinv1alpha2 "github.com/kyma-project/kyma/components/eventing-controller/api/v1alpha2" - v1 "k8s.io/api/apps/v1" ) const ( @@ -243,7 +243,6 @@ func Test_UpdateEventingCR(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { - t.Parallel() // given eventingcontroller.IsDeploymentReady = func(deployment *v1.Deployment) bool { return true @@ -293,6 +292,88 @@ func Test_UpdateEventingCR(t *testing.T) { } } +func Test_ReconcileSameEventingCR(t *testing.T) { + t.Parallel() + + //// + // given + //// + eventingcontroller.IsDeploymentReady = func(deployment *v1.Deployment) bool { return true } + + eventingCR := utils.NewEventingCR( + utils.WithEventingCRMinimal(), + utils.WithEventingStreamData("Memory", "1M", 1, 1), + utils.WithEventingPublisherData(1, 1, "199m", "99Mi", "399m", "199Mi"), + ) + + namespace := eventingCR.GetNamespace() + + natsCR := natstestutils.NewNATSCR( + natstestutils.WithNATSCRNamespace(namespace), + natstestutils.WithNATSCRDefaults(), + natstestutils.WithNATSStateReady(), + ) + + testEnvironment.EnsureNamespaceCreation(t, namespace) + testEnvironment.EnsureK8sResourceCreated(t, natsCR) + testEnvironment.EnsureNATSResourceStateReady(t, natsCR) + testEnvironment.EnsureK8sResourceCreated(t, eventingCR) + + eppDeploymentName := eventing.GetPublisherDeploymentName(*eventingCR) + testEnvironment.EnsureDeploymentExists(t, eppDeploymentName, namespace) + testEnvironment.EnsureHPAExists(t, eppDeploymentName, namespace) + + eppDeployment, err := testEnvironment.GetDeploymentFromK8s(eppDeploymentName, namespace) + require.NoError(t, err) + require.NotNil(t, eppDeployment) + + defer func() { + testEnvironment.EnsureEventingResourceDeletion(t, eventingCR.Name, namespace) + if !*testEnvironment.EnvTestInstance.UseExistingCluster { + testEnvironment.EnsureDeploymentDeletion(t, eppDeploymentName, namespace) + } + testEnvironment.EnsureK8sResourceDeleted(t, natsCR) + testEnvironment.EnsureNamespaceDeleted(t, namespace) + }() + + // Ensure reconciling the same Eventing CR multiple times does not update the EPP deployment. + const runs = 3 + var resourceVersionBefore = eppDeployment.ObjectMeta.ResourceVersion + for r := 0; r < runs; r++ { + //// + // when + //// + runId := fmt.Sprintf("run-%d", r) + + eventingCR, err = testEnvironment.GetEventingFromK8s(eventingCR.Name, namespace) + require.NoError(t, err) + require.NotNil(t, eventingCR) + + eventingCR = eventingCR.DeepCopy() + eventingCR.ObjectMeta.Labels = map[string]string{"reconcile": runId} // force new reconciliation + testEnvironment.EnsureK8sResourceUpdated(t, eventingCR) + + eventingCR, err = testEnvironment.GetEventingFromK8s(eventingCR.Name, namespace) + require.NoError(t, err) + require.NotNil(t, eventingCR) + require.Equal(t, eventingCR.ObjectMeta.Labels["reconcile"], runId) + + //// + // then + //// + testEnvironment.EnsureEventingSpecPublisherReflected(t, eventingCR) + testEnvironment.EnsureEventingReplicasReflected(t, eventingCR) + testEnvironment.EnsureDeploymentOwnerReferenceSet(t, eventingCR) + + eppDeployment, err = testEnvironment.GetDeploymentFromK8s(eppDeploymentName, namespace) + require.NoError(t, err) + require.NotNil(t, eppDeployment) + + resourceVersionAfter := eppDeployment.ObjectMeta.ResourceVersion + require.Equal(t, resourceVersionBefore, resourceVersionAfter) + } +} + // Test_WatcherEventingCRK8sObjects tests that deleting the k8s objects deployed by Eventing CR // should trigger reconciliation. func Test_WatcherEventingCRK8sObjects(t *testing.T) { diff --git a/internal/controller/subscription/eventmesh/test/reconciler_integration_test.go b/internal/controller/subscription/eventmesh/test/reconciler_integration_test.go index cacf2cb8..808d6556 100644 --- a/internal/controller/subscription/eventmesh/test/reconciler_integration_test.go +++ b/internal/controller/subscription/eventmesh/test/reconciler_integration_test.go @@ -16,11 +16,12 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" apigatewayv1beta1 "github.com/kyma-incubator/api-gateway/api/v1beta1" + eventingv1alpha2 "github.com/kyma-project/kyma/components/eventing-controller/api/v1alpha2" + eventMeshtypes "github.com/kyma-project/eventing-manager/pkg/ems/api/events/types" "github.com/kyma-project/eventing-manager/pkg/object" reconcilertesting "github.com/kyma-project/eventing-manager/testing" eventmeshsubmatchers "github.com/kyma-project/eventing-manager/testing/eventmeshsub" - eventingv1alpha2 "github.com/kyma-project/kyma/components/eventing-controller/api/v1alpha2" ) const ( @@ -132,7 +133,6 @@ func Test_ValidationWebhook(t *testing.T) { } func Test_CreateSubscription(t *testing.T) { - t.Parallel() var testCases = []struct { name string givenSubscriptionFunc func(namespace string) *eventingv1alpha2.Subscription @@ -304,7 +304,6 @@ func Test_CreateSubscription(t *testing.T) { for _, testCase := range testCases { tc := testCase t.Run(tc.name, func(t *testing.T) { - t.Parallel() g := gomega.NewGomegaWithT(t) ctx := context.Background() diff --git a/internal/controller/subscription/eventmesh/testwithory/reconciler_integration_test.go b/internal/controller/subscription/eventmesh/testwithory/reconciler_integration_test.go index 8b1132c4..3e11e7b6 100644 --- a/internal/controller/subscription/eventmesh/testwithory/reconciler_integration_test.go +++ b/internal/controller/subscription/eventmesh/testwithory/reconciler_integration_test.go @@ -16,11 +16,12 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" apigatewayv1beta1 "github.com/kyma-incubator/api-gateway/api/v1beta1" + eventingv1alpha2 "github.com/kyma-project/kyma/components/eventing-controller/api/v1alpha2" + eventMeshtypes "github.com/kyma-project/eventing-manager/pkg/ems/api/events/types" "github.com/kyma-project/eventing-manager/pkg/object" reconcilertesting "github.com/kyma-project/eventing-manager/testing" eventmeshsubmatchers "github.com/kyma-project/eventing-manager/testing/eventmeshsub" - eventingv1alpha2 "github.com/kyma-project/kyma/components/eventing-controller/api/v1alpha2" ) const ( @@ -132,7 +133,6 @@ func Test_ValidationWebhook(t *testing.T) { } func Test_CreateSubscription(t *testing.T) { - t.Parallel() var testCases = []struct { name string givenSubscriptionFunc func(namespace string) *eventingv1alpha2.Subscription @@ -304,7 +304,6 @@ func Test_CreateSubscription(t *testing.T) { for _, testCase := range testCases { tc := testCase t.Run(tc.name, func(t *testing.T) { - t.Parallel() g := gomega.NewGomegaWithT(t) ctx := context.Background() diff --git a/pkg/eventing/deployment.go b/pkg/eventing/deployment.go index 5b30a545..a12dd234 100644 --- a/pkg/eventing/deployment.go +++ b/pkg/eventing/deployment.go @@ -50,7 +50,8 @@ var ( func newNATSPublisherDeployment( eventing *v1alpha1.Eventing, natsConfig env.NATSConfig, - publisherConfig env.PublisherConfig) *appsv1.Deployment { + publisherConfig env.PublisherConfig, +) *appsv1.Deployment { return newDeployment( eventing, publisherConfig, @@ -65,7 +66,8 @@ func newNATSPublisherDeployment( func newEventMeshPublisherDeployment( eventing *v1alpha1.Eventing, - publisherConfig env.PublisherConfig) *appsv1.Deployment { + publisherConfig env.PublisherConfig, +) *appsv1.Deployment { return newDeployment( eventing, publisherConfig, diff --git a/pkg/eventing/manager.go b/pkg/eventing/manager.go index aacc4883..06e2ac8b 100644 --- a/pkg/eventing/manager.go +++ b/pkg/eventing/manager.go @@ -4,10 +4,9 @@ import ( "context" "fmt" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - ecv1alpha1 "github.com/kyma-project/kyma/components/eventing-controller/api/v1alpha1" appsv1 "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -16,6 +15,7 @@ import ( "github.com/kyma-project/eventing-manager/pkg/env" "github.com/kyma-project/eventing-manager/pkg/k8s" "github.com/kyma-project/eventing-manager/pkg/logger" + "github.com/kyma-project/eventing-manager/pkg/object" ) const ( @@ -125,6 +125,14 @@ func (em *EventingManager) applyPublisherProxyDeployment( return nil, fmt.Errorf("failed to migrate publisher: %v", err) } } + + if object.Semantic.DeepEqual(currentPublisher, desiredPublisher) { + em.logger.WithContext().Debug( + "skip updating the EPP deployment because its desired and actual states are equal", + ) + return currentPublisher, nil + } + // Update publisher proxy deployment if err := em.kubeClient.PatchApply(ctx, desiredPublisher); err != nil { return nil, fmt.Errorf("failed to apply Publisher Proxy deployment: %v", err) @@ -206,14 +214,15 @@ func (em EventingManager) DeployPublisherProxyResources( } // create the resources on k8s. - for _, object := range resources { + for _, obj := range resources { + // add owner reference. - if err := controllerutil.SetControllerReference(eventing, object, em.Scheme()); err != nil { + if err := controllerutil.SetControllerReference(eventing, obj, em.Scheme()); err != nil { return err } // patch apply the object. - if err := em.kubeClient.PatchApply(ctx, object); err != nil { + if err := em.kubeClient.PatchApply(ctx, obj); err != nil { return err } } diff --git a/pkg/eventing/manager_test.go b/pkg/eventing/manager_test.go index a830c232..3facc851 100644 --- a/pkg/eventing/manager_test.go +++ b/pkg/eventing/manager_test.go @@ -8,18 +8,15 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" - "github.com/kyma-project/eventing-manager/test" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" + "github.com/kyma-project/eventing-manager/test" + "sigs.k8s.io/controller-runtime/pkg/client" "k8s.io/apimachinery/pkg/runtime" - "github.com/kyma-project/eventing-manager/api/v1alpha1" - "github.com/kyma-project/eventing-manager/pkg/env" - k8smocks "github.com/kyma-project/eventing-manager/pkg/k8s/mocks" - testutils "github.com/kyma-project/eventing-manager/test/utils" ecv1alpha1 "github.com/kyma-project/kyma/components/eventing-controller/api/v1alpha1" eventingv1alpha2 "github.com/kyma-project/kyma/components/eventing-controller/api/v1alpha2" natsv1alpha1 "github.com/kyma-project/nats-manager/api/v1alpha1" @@ -27,6 +24,11 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" + + "github.com/kyma-project/eventing-manager/api/v1alpha1" + "github.com/kyma-project/eventing-manager/pkg/env" + k8smocks "github.com/kyma-project/eventing-manager/pkg/k8s/mocks" + testutils "github.com/kyma-project/eventing-manager/test/utils" ) func Test_ApplyPublisherProxyDeployment(t *testing.T) { diff --git a/pkg/k8s/client.go b/pkg/k8s/client.go index bbb28b5c..ab39af5b 100644 --- a/pkg/k8s/client.go +++ b/pkg/k8s/client.go @@ -5,11 +5,6 @@ import ( "errors" "strings" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - - eventingv1alpha2 "github.com/kyma-project/kyma/components/eventing-controller/api/v1alpha2" - - natsv1alpha1 "github.com/kyma-project/nats-manager/api/v1alpha1" istiosec "istio.io/client-go/pkg/apis/security/v1beta1" admissionv1 "k8s.io/api/admissionregistration/v1" v1 "k8s.io/api/apps/v1" @@ -18,11 +13,15 @@ import ( apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" k8sclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" + + eventingv1alpha2 "github.com/kyma-project/kyma/components/eventing-controller/api/v1alpha2" + natsv1alpha1 "github.com/kyma-project/nats-manager/api/v1alpha1" ) var NatsGVK = schema.GroupVersionResource{ diff --git a/pkg/k8s/client_test.go b/pkg/k8s/client_test.go index 2f9f60e4..a5a48135 100644 --- a/pkg/k8s/client_test.go +++ b/pkg/k8s/client_test.go @@ -11,9 +11,10 @@ import ( "k8s.io/apimachinery/pkg/runtime" - testutils "github.com/kyma-project/eventing-manager/test/utils" eventingv1alpha2 "github.com/kyma-project/kyma/components/eventing-controller/api/v1alpha2" + testutils "github.com/kyma-project/eventing-manager/test/utils" + admissionv1 "k8s.io/api/admissionregistration/v1" apiclientsetfake "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake" diff --git a/pkg/object/equality.go b/pkg/object/equality.go index 1ff00b86..92a67f86 100644 --- a/pkg/object/equality.go +++ b/pkg/object/equality.go @@ -4,7 +4,9 @@ import ( "reflect" appsv1 "k8s.io/api/apps/v1" + v2 "k8s.io/api/autoscaling/v2" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/conversion" @@ -19,18 +21,147 @@ var Semantic = conversion.EqualitiesOrDie( apiRuleEqual, eventingBackendEqual, publisherProxyDeploymentEqual, + serviceAccountEqual, + clusterRoleEqual, + clusterRoleBindingEqual, + serviceEqual, + hpaEqual, ) -// apiRuleEqual asserts the equality of two APIRule objects. -func apiRuleEqual(a1, a2 *apigatewayv1beta1.APIRule) bool { - if a1 == nil || a2 == nil { +func serviceAccountEqual(a, b *corev1.ServiceAccount) bool { + if a == b { + return true + } + if a == nil || b == nil { + return false + } + + if a.Name != b.Name || a.Namespace != b.Namespace { + return false + } + + if !mapDeepEqual(a.ObjectMeta.Labels, b.ObjectMeta.Labels) { + return false + } + + if !ownerReferencesDeepEqual(a.OwnerReferences, b.OwnerReferences) { + return false + } + + return true +} + +func clusterRoleEqual(a, b *rbacv1.ClusterRole) bool { + if a == b { + return true + } + if a == nil || b == nil { + return false + } + + if a.Name != b.Name { + return false + } + + if !mapDeepEqual(a.Labels, b.Labels) { + return false + } + + if !ownerReferencesDeepEqual(a.OwnerReferences, b.OwnerReferences) { + return false + } + + if !reflect.DeepEqual(a.Rules, b.Rules) { + return false + } + return true +} + +func serviceEqual(a, b *corev1.Service) bool { + if a == b { + return true + } + if a == nil || b == nil { + return false + } + if !ownerReferencesDeepEqual(a.OwnerReferences, b.OwnerReferences) { + return false + } + if a.Name != b.Name || a.Namespace != b.Namespace { + return false + } + if !reflect.DeepEqual(a.Spec.Selector, b.Spec.Selector) { + return false + } + return reflect.DeepEqual(a.Spec.Ports, b.Spec.Ports) +} + +func hpaEqual(a, b *v2.HorizontalPodAutoscaler) bool { + if a == b { + return true + } + if a == nil || b == nil { + return false + } + if a.Name != b.Name || a.Namespace != b.Namespace { + return false + } + + if !ownerReferencesDeepEqual(a.OwnerReferences, b.OwnerReferences) { + return false + } + + if !reflect.DeepEqual(a.Spec.ScaleTargetRef, b.Spec.ScaleTargetRef) { + return false + } + if *(a.Spec.MinReplicas) != *(b.Spec.MinReplicas) { + return false + } + if a.Spec.MaxReplicas != b.Spec.MaxReplicas { + return false + } + if !reflect.DeepEqual(a.Spec.Metrics, b.Spec.Metrics) { + return false + } + + return true +} + +func clusterRoleBindingEqual(a, b *rbacv1.ClusterRoleBinding) bool { + if a == b { + return true + } + if a == nil || b == nil { + return false + } + if a.Name != b.Name { return false } + if !ownerReferencesDeepEqual(a.OwnerReferences, b.OwnerReferences) { + return false + } + + if a.RoleRef != b.RoleRef { + return false + } + + if !reflect.DeepEqual(a.Subjects, b.Subjects) { + return false + } + return true +} + +// apiRuleEqual asserts the equality of two APIRule objects. +func apiRuleEqual(a1, a2 *apigatewayv1beta1.APIRule) bool { if a1 == a2 { return true } + if a1 == nil || a2 == nil { + return false + } + if !reflect.DeepEqual(a1.Labels, a2.Labels) { return false } @@ -110,9 +241,6 @@ func publisherProxyDeploymentEqual(d1, d2 *appsv1.Deployment) bool { if !reflect.DeepEqual(d1.Labels, d2.Labels) { return false } - if !reflect.DeepEqual(d1.Spec.Replicas, d2.Spec.Replicas) { - return false - } cst1 := d1.Spec.Template cst2 := d2.Spec.Template @@ -193,6 +321,10 @@ func containerEqual(c1, c2 *corev1.Container) bool { return false } + if !reflect.DeepEqual(c1.Resources, c2.Resources) { + return false + } + return probeEqual(c1.ReadinessProbe, c2.ReadinessProbe) } @@ -202,8 +334,13 @@ func envEqual(e1, e2 []corev1.EnvVar) bool { if len(e1) != len(e2) { return false } + + if len(e1) == 0 { + return true + } isFound := false for _, ev1 := range e1 { + isFound = false for _, ev2 := range e2 { if reflect.DeepEqual(ev1, ev2) { isFound = true diff --git a/pkg/object/equality_test.go b/pkg/object/equality_test.go index 47e3f6d2..8452e7dc 100644 --- a/pkg/object/equality_test.go +++ b/pkg/object/equality_test.go @@ -4,19 +4,25 @@ import ( "net/http" "testing" + "k8s.io/apimachinery/pkg/util/intstr" + "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" + v2 "k8s.io/api/autoscaling/v2" corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/utils/ptr" apigatewayv1beta1 "github.com/kyma-incubator/api-gateway/api/v1beta1" - "github.com/kyma-project/eventing-manager/pkg/utils" eventingv1alpha1 "github.com/kyma-project/kyma/components/eventing-controller/api/v1alpha1" eventingv1alpha2 "github.com/kyma-project/kyma/components/eventing-controller/api/v1alpha2" "github.com/kyma-project/kyma/components/eventing-controller/pkg/deployment" "github.com/kyma-project/kyma/components/eventing-controller/pkg/env" + + "github.com/kyma-project/eventing-manager/pkg/utils" ) func TestApiRuleEqual(t *testing.T) { @@ -531,7 +537,22 @@ func TestPublisherProxyDeploymentEqual(t *testing.T) { }, expectedResult: false, }, - "should be unequal if replicas changes": { + "should be equal if replicas changes": { + getPublisher1: func() *appsv1.Deployment { + replicas := int32(1) + p := defaultNATSPublisher.DeepCopy() + p.Spec.Replicas = &replicas + return p + }, + getPublisher2: func() *appsv1.Deployment { + replicas := int32(2) + p := defaultNATSPublisher.DeepCopy() + p.Spec.Replicas = &replicas + return p + }, + expectedResult: true, + }, + "should be equal if replicas are the same": { getPublisher1: func() *appsv1.Deployment { replicas := int32(2) p := defaultNATSPublisher.DeepCopy() @@ -539,9 +560,12 @@ func TestPublisherProxyDeploymentEqual(t *testing.T) { return p }, getPublisher2: func() *appsv1.Deployment { - return defaultNATSPublisher.DeepCopy() + replicas := int32(2) + p := defaultNATSPublisher.DeepCopy() + p.Spec.Replicas = &replicas + return p }, - expectedResult: false, + expectedResult: true, }, "should be equal if spec annotations are nil and empty": { getPublisher1: func() *appsv1.Deployment { @@ -732,3 +756,1350 @@ func Test_ownerReferencesDeepEqual(t *testing.T) { }) } } + +func Test_containerEqual(t *testing.T) { + quantityA, _ := resource.ParseQuantity("5m") + quantityB, _ := resource.ParseQuantity("10k") + + type args struct { + c1 *corev1.Container + c2 *corev1.Container + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "container are equal", + args: args{ + c1: &corev1.Container{ + Name: "test", + Image: "bla", + Command: []string{"1", "2"}, + Args: []string{"a", "b"}, + WorkingDir: "foodir", + Ports: []corev1.ContainerPort{{ + Name: "testport", + HostPort: 1, + ContainerPort: 2, + Protocol: "http", + HostIP: "192.168.1.1", + }}, + Resources: corev1.ResourceRequirements{ + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": quantityA, + }, + Requests: map[corev1.ResourceName]resource.Quantity{ + "mem": quantityA, + }, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{}, + InitialDelaySeconds: 0, + TimeoutSeconds: 0, + PeriodSeconds: 0, + SuccessThreshold: 0, + FailureThreshold: 0, + }, + }, + c2: &corev1.Container{ + Name: "test", + Image: "bla", + Command: []string{"1", "2"}, + Args: []string{"a", "b"}, + WorkingDir: "foodir", + Ports: []corev1.ContainerPort{{ + Name: "testport", + HostPort: 1, + ContainerPort: 2, + Protocol: "http", + HostIP: "192.168.1.1", + }}, + Resources: corev1.ResourceRequirements{ + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": quantityA, + }, + Requests: map[corev1.ResourceName]resource.Quantity{ + "mem": quantityA, + }, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{}, + InitialDelaySeconds: 0, + TimeoutSeconds: 0, + PeriodSeconds: 0, + SuccessThreshold: 0, + FailureThreshold: 0, + }, + }, + }, + want: true, + }, + { + name: "resources are not equal", + args: args{ + c1: &corev1.Container{ + Name: "test", + Image: "bla", + Command: []string{"1", "2"}, + Args: []string{"a", "b"}, + WorkingDir: "foodir", + Ports: []corev1.ContainerPort{{ + Name: "testport", + HostPort: 1, + ContainerPort: 2, + Protocol: "http", + HostIP: "192.168.1.1", + }}, + Resources: corev1.ResourceRequirements{ + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": quantityA, + }, + Requests: map[corev1.ResourceName]resource.Quantity{ + "mem": quantityA, + }, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{}, + InitialDelaySeconds: 0, + TimeoutSeconds: 0, + PeriodSeconds: 0, + SuccessThreshold: 0, + FailureThreshold: 0, + }, + }, + c2: &corev1.Container{ + Name: "test", + Image: "bla", + Command: []string{"1", "2"}, + Args: []string{"a", "b"}, + WorkingDir: "foodir", + Ports: []corev1.ContainerPort{{ + Name: "testport", + HostPort: 1, + ContainerPort: 2, + Protocol: "http", + HostIP: "192.168.1.1", + }}, + Resources: corev1.ResourceRequirements{ + Limits: map[corev1.ResourceName]resource.Quantity{ + "cpu": quantityB, + }, + Requests: map[corev1.ResourceName]resource.Quantity{ + "mem": quantityB, + }, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{}, + InitialDelaySeconds: 0, + TimeoutSeconds: 0, + PeriodSeconds: 0, + SuccessThreshold: 0, + FailureThreshold: 0, + }, + }, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := containerEqual(tt.args.c1, tt.args.c2); got != tt.want { + t.Errorf("containerEqual() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_serviceAccountEqual(t *testing.T) { + const ( + name0 = "name-0" + name1 = "name-1" + + namespace0 = "ns-0" + namespace1 = "ns-1" + ) + + var ( + labels0 = map[string]string{"key": "val-0"} + labels1 = map[string]string{"key": "val-1"} + + ownerReferences0 = []metav1.OwnerReference{ + { + Name: "name-0", + APIVersion: "version-0", + Kind: "kind-0", + UID: "000000", + Controller: ptr.To(false), + BlockOwnerDeletion: ptr.To(false), + }, + } + ownerReferences1 = []metav1.OwnerReference{ + { + Name: "name-1", + APIVersion: "version-0", + Kind: "kind-0", + UID: "000000", + Controller: ptr.To(false), + BlockOwnerDeletion: ptr.To(false), + }, + } + ) + + type args struct { + a *corev1.ServiceAccount + b *corev1.ServiceAccount + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "ServiceAccounts are equal", + args: args{ + a: &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + Labels: labels0, + OwnerReferences: ownerReferences0, + }, + }, + b: &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + Labels: labels0, + OwnerReferences: ownerReferences0, + }, + }, + }, + want: true, + }, + { + name: "ServiceAccount names are not equal", + args: args{ + a: &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + Labels: labels0, + OwnerReferences: ownerReferences0, + }, + }, + b: &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name1, + Namespace: namespace0, + Labels: labels0, + OwnerReferences: ownerReferences0, + }, + }, + }, + want: false, + }, + { + name: "ServiceAccount namespaces are not equal", + args: args{ + a: &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + Labels: labels0, + OwnerReferences: ownerReferences0, + }, + }, + b: &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace1, + Labels: labels0, + OwnerReferences: ownerReferences0, + }, + }, + }, + want: false, + }, + { + name: "ServiceAccount labels are not equal", + args: args{ + a: &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + Labels: labels0, + OwnerReferences: ownerReferences0, + }, + }, + b: &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + Labels: labels1, + OwnerReferences: ownerReferences0, + }, + }, + }, + want: false, + }, + { + name: "ServiceAccount OwnerReferences are not equal", + args: args{ + a: &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + Labels: labels0, + OwnerReferences: ownerReferences0, + }, + }, + b: &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + Labels: labels0, + OwnerReferences: ownerReferences1, + }, + }, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := serviceAccountEqual(tt.args.a, tt.args.b); got != tt.want { + t.Errorf("serviceAccountEqual() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_clusterRoleEqual(t *testing.T) { + const ( + name0 = "name-0" + name1 = "name-1" + ) + + var ( + labels0 = map[string]string{"key": "val-0"} + labels1 = map[string]string{"key": "val-1"} + + ownerReferences0 = []metav1.OwnerReference{ + { + Name: "name-0", + APIVersion: "version-0", + Kind: "kind-0", + UID: "000000", + Controller: ptr.To(false), + BlockOwnerDeletion: ptr.To(false), + }, + } + ownerReferences1 = []metav1.OwnerReference{ + { + Name: "name-1", + APIVersion: "version-0", + Kind: "kind-0", + UID: "000000", + Controller: ptr.To(false), + BlockOwnerDeletion: ptr.To(false), + }, + } + + rules0 = []rbacv1.PolicyRule{ + { + Verbs: []string{"val-0"}, + APIGroups: []string{"val-0"}, + Resources: []string{"val-0"}, + ResourceNames: []string{"val-0"}, + NonResourceURLs: []string{"val-0"}, + }, + } + rules1 = []rbacv1.PolicyRule{ + { + Verbs: []string{"val-1"}, + APIGroups: []string{"val-0"}, + Resources: []string{"val-0"}, + ResourceNames: []string{"val-0"}, + NonResourceURLs: []string{"val-0"}, + }, + } + ) + + type args struct { + a *rbacv1.ClusterRole + b *rbacv1.ClusterRole + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "ClusterRoles are equal", + args: args{ + a: &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Labels: labels0, + OwnerReferences: ownerReferences0, + }, + Rules: rules0, + }, + b: &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Labels: labels0, + OwnerReferences: ownerReferences0, + }, + Rules: rules0, + }, + }, + want: true, + }, + { + name: "ClusterRole names are not equal", + args: args{ + a: &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Labels: labels0, + OwnerReferences: ownerReferences0, + }, + Rules: rules0, + }, + b: &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: name1, + Labels: labels0, + OwnerReferences: ownerReferences0, + }, + Rules: rules0, + }, + }, + want: false, + }, + { + name: "ClusterRole labels are not equal", + args: args{ + a: &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Labels: labels0, + OwnerReferences: ownerReferences0, + }, + Rules: rules0, + }, + b: &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Labels: labels1, + OwnerReferences: ownerReferences0, + }, + Rules: rules0, + }, + }, + want: false, + }, + { + name: "ClusterRole OwnerReferences are not equal", + args: args{ + a: &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Labels: labels0, + OwnerReferences: ownerReferences0, + }, + Rules: rules0, + }, + b: &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Labels: labels0, + OwnerReferences: ownerReferences1, + }, + Rules: rules0, + }, + }, + want: false, + }, + { + name: "ClusterRole Rules are not equal", + args: args{ + a: &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Labels: labels0, + OwnerReferences: ownerReferences0, + }, + Rules: rules0, + }, + b: &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Labels: labels0, + OwnerReferences: ownerReferences0, + }, + Rules: rules1, + }, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := clusterRoleEqual(tt.args.a, tt.args.b); got != tt.want { + t.Errorf("clusterRoleEqual() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_clusterRoleBindingEqual(t *testing.T) { + const ( + name0 = "name-0" + name1 = "name-1" + ) + + var ( + ownerReferences0 = []metav1.OwnerReference{ + { + Name: "name-0", + APIVersion: "version-0", + Kind: "kind-0", + UID: "000000", + Controller: ptr.To(false), + BlockOwnerDeletion: ptr.To(false), + }, + } + ownerReferences1 = []metav1.OwnerReference{ + { + Name: "name-1", + APIVersion: "version-0", + Kind: "kind-0", + UID: "000000", + Controller: ptr.To(false), + BlockOwnerDeletion: ptr.To(false), + }, + } + + subjects0 = []rbacv1.Subject{ + { + Kind: "kind-0", + APIGroup: "group-0", + Name: "name-0", + Namespace: "namespace-0", + }, + } + subjects1 = []rbacv1.Subject{ + { + Kind: "kind-1", + APIGroup: "group-0", + Name: "name-0", + Namespace: "namespace-0", + }, + } + + roleRef0 = rbacv1.RoleRef{ + APIGroup: "group-0", + Kind: "kind-0", + Name: "name-0", + } + roleRef1 = rbacv1.RoleRef{ + APIGroup: "group-1", + Kind: "kind-0", + Name: "name-0", + } + ) + + type args struct { + a *rbacv1.ClusterRoleBinding + b *rbacv1.ClusterRoleBinding + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "ClusterRoleBindings are equal", + args: args{ + a: &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + OwnerReferences: ownerReferences0, + }, + Subjects: subjects0, + RoleRef: roleRef0, + }, + b: &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + OwnerReferences: ownerReferences0, + }, + Subjects: subjects0, + RoleRef: roleRef0, + }, + }, + want: true, + }, + { + name: "ClusterRoleBinding names are not equal", + args: args{ + a: &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + OwnerReferences: ownerReferences0, + }, + Subjects: subjects0, + RoleRef: roleRef0, + }, + b: &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: name1, + OwnerReferences: ownerReferences0, + }, + Subjects: subjects0, + RoleRef: roleRef0, + }, + }, + want: false, + }, + { + name: "ClusterRoleBinding OwnerReferences are not equal", + args: args{ + a: &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + OwnerReferences: ownerReferences0, + }, + Subjects: subjects0, + RoleRef: roleRef0, + }, + b: &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + OwnerReferences: ownerReferences1, + }, + Subjects: subjects0, + RoleRef: roleRef0, + }, + }, + want: false, + }, + { + name: "ClusterRoleBinding Subjects are not equal", + args: args{ + a: &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + OwnerReferences: ownerReferences0, + }, + Subjects: subjects0, + RoleRef: roleRef0, + }, + b: &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + OwnerReferences: ownerReferences0, + }, + Subjects: subjects1, + RoleRef: roleRef0, + }, + }, + want: false, + }, + { + name: "ClusterRoleBinding RoleRefs are not equal", + args: args{ + a: &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + OwnerReferences: ownerReferences0, + }, + Subjects: subjects0, + RoleRef: roleRef0, + }, + b: &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + OwnerReferences: ownerReferences0, + }, + Subjects: subjects0, + RoleRef: roleRef1, + }, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := clusterRoleBindingEqual(tt.args.a, tt.args.b); got != tt.want { + t.Errorf("clusterRoleBindingEqual() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_serviceEqual(t *testing.T) { + const ( + name0 = "name-0" + name1 = "name-1" + + namespace0 = "namespace0" + namespace1 = "namespace1" + ) + + var ( + ownerReferences0 = []metav1.OwnerReference{ + { + Name: "name-0", + APIVersion: "version-0", + Kind: "kind-0", + UID: "000000", + Controller: ptr.To(false), + BlockOwnerDeletion: ptr.To(false), + }, + } + ownerReferences1 = []metav1.OwnerReference{ + { + Name: "name-1", + APIVersion: "version-0", + Kind: "kind-0", + UID: "000000", + Controller: ptr.To(false), + BlockOwnerDeletion: ptr.To(false), + }, + } + + ports0 = []corev1.ServicePort{ + { + Name: "name-0", + Protocol: "protocol-0", + AppProtocol: nil, + Port: 0, + TargetPort: intstr.IntOrString{ + Type: 0, + IntVal: 0, + StrVal: "val-0", + }, + NodePort: 0, + }, + } + ports1 = []corev1.ServicePort{ + { + Name: "name-1", + Protocol: "protocol-0", + AppProtocol: nil, + Port: 0, + TargetPort: intstr.IntOrString{ + Type: 0, + IntVal: 0, + StrVal: "val-0", + }, + NodePort: 0, + }, + } + + selector0 = map[string]string{ + "key": "val-0", + } + selector1 = map[string]string{ + "key": "val-1", + } + ) + + type args struct { + a *corev1.Service + b *corev1.Service + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "Services are equal", + args: args{ + a: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: corev1.ServiceSpec{ + Ports: ports0, + Selector: selector0, + }, + }, + b: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: corev1.ServiceSpec{ + Ports: ports0, + Selector: selector0, + }, + }, + }, + want: true, + }, + { + name: "Service names are not equal", + args: args{ + a: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: corev1.ServiceSpec{ + Ports: ports0, + Selector: selector0, + }, + }, + b: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name1, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: corev1.ServiceSpec{ + Ports: ports0, + Selector: selector0, + }, + }, + }, + want: false, + }, + { + name: "Service namespaces are not equal", + args: args{ + a: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: corev1.ServiceSpec{ + Ports: ports0, + Selector: selector0, + }, + }, + b: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace1, + OwnerReferences: ownerReferences0, + }, + Spec: corev1.ServiceSpec{ + Ports: ports0, + Selector: selector0, + }, + }, + }, + want: false, + }, + { + name: "Service OwnerReferences are not equal", + args: args{ + a: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: corev1.ServiceSpec{ + Ports: ports0, + Selector: selector0, + }, + }, + b: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences1, + }, + Spec: corev1.ServiceSpec{ + Ports: ports0, + Selector: selector0, + }, + }, + }, + want: false, + }, + { + name: "Service ports are not equal", + args: args{ + a: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: corev1.ServiceSpec{ + Ports: ports0, + Selector: selector0, + }, + }, + b: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: corev1.ServiceSpec{ + Ports: ports1, + Selector: selector0, + }, + }, + }, + want: false, + }, + { + name: "Service selectors are not equal", + args: args{ + a: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: corev1.ServiceSpec{ + Ports: ports0, + Selector: selector0, + }, + }, + b: &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: corev1.ServiceSpec{ + Ports: ports0, + Selector: selector1, + }, + }, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := serviceEqual(tt.args.a, tt.args.b); got != tt.want { + t.Errorf("serviceEqual() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_hpaEqual(t *testing.T) { + const ( + name0 = "name-0" + name1 = "name-1" + + namespace0 = "ns-0" + namespace1 = "ns-1" + ) + + var ( + ownerReferences0 = []metav1.OwnerReference{ + { + Name: "name-0", + APIVersion: "version-0", + Kind: "kind-0", + UID: "000000", + Controller: ptr.To(false), + BlockOwnerDeletion: ptr.To(false), + }, + } + ownerReferences1 = []metav1.OwnerReference{ + { + Name: "name-1", + APIVersion: "version-0", + Kind: "kind-0", + UID: "000000", + Controller: ptr.To(false), + BlockOwnerDeletion: ptr.To(false), + }, + } + + scaleTargetRef0 = v2.CrossVersionObjectReference{ + Name: "name-0", + Kind: "kind-0", + APIVersion: "version-0", + } + scaleTargetRef1 = v2.CrossVersionObjectReference{ + Name: "name-1", + Kind: "kind-0", + APIVersion: "version-0", + } + + minReplicas0 = ptr.To(int32(1)) + minReplicas1 = ptr.To(int32(2)) + + maxReplicas0 = int32(3) + maxReplicas1 = int32(4) + + metrics0 = []v2.MetricSpec{ + { + Type: v2.MetricSourceType("type-0"), + Object: nil, + Pods: nil, + Resource: nil, + ContainerResource: nil, + External: nil, + }, + } + metrics1 = []v2.MetricSpec{ + { + Type: v2.MetricSourceType("type-1"), + Object: nil, + Pods: nil, + Resource: nil, + ContainerResource: nil, + External: nil, + }, + } + ) + + type args struct { + a *v2.HorizontalPodAutoscaler + b *v2.HorizontalPodAutoscaler + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "HPAs are equal", + args: args{ + a: &v2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: v2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: scaleTargetRef0, + MinReplicas: minReplicas0, + MaxReplicas: maxReplicas0, + Metrics: metrics0, + }, + }, + b: &v2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: v2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: scaleTargetRef0, + MinReplicas: minReplicas0, + MaxReplicas: maxReplicas0, + Metrics: metrics0, + }, + }, + }, + want: true, + }, + { + name: "HPA names are not equal", + args: args{ + a: &v2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: v2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: scaleTargetRef0, + MinReplicas: minReplicas0, + MaxReplicas: maxReplicas0, + Metrics: metrics0, + }, + }, + b: &v2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: name1, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: v2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: scaleTargetRef0, + MinReplicas: minReplicas0, + MaxReplicas: maxReplicas0, + Metrics: metrics0, + }, + }, + }, + want: false, + }, + { + name: "HPA namespaces are not equal", + args: args{ + a: &v2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: v2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: scaleTargetRef0, + MinReplicas: minReplicas0, + MaxReplicas: maxReplicas0, + Metrics: metrics0, + }, + }, + b: &v2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace1, + OwnerReferences: ownerReferences0, + }, + Spec: v2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: scaleTargetRef0, + MinReplicas: minReplicas0, + MaxReplicas: maxReplicas0, + Metrics: metrics0, + }, + }, + }, + want: false, + }, + { + name: "HPA OwnerReferences are not equal", + args: args{ + a: &v2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: v2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: scaleTargetRef0, + MinReplicas: minReplicas0, + MaxReplicas: maxReplicas0, + Metrics: metrics0, + }, + }, + b: &v2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences1, + }, + Spec: v2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: scaleTargetRef0, + MinReplicas: minReplicas0, + MaxReplicas: maxReplicas0, + Metrics: metrics0, + }, + }, + }, + want: false, + }, + { + name: "HPA ScaleTargetRefs are not equal", + args: args{ + a: &v2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: v2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: scaleTargetRef0, + MinReplicas: minReplicas0, + MaxReplicas: maxReplicas0, + Metrics: metrics0, + }, + }, + b: &v2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: v2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: scaleTargetRef1, + MinReplicas: minReplicas0, + MaxReplicas: maxReplicas0, + Metrics: metrics0, + }, + }, + }, + want: false, + }, + { + name: "HPA MinReplicas are not equal", + args: args{ + a: &v2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: v2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: scaleTargetRef0, + MinReplicas: minReplicas0, + MaxReplicas: maxReplicas0, + Metrics: metrics0, + }, + }, + b: &v2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: v2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: scaleTargetRef0, + MinReplicas: minReplicas1, + MaxReplicas: maxReplicas0, + Metrics: metrics0, + }, + }, + }, + want: false, + }, + { + name: "HPA MaxReplicas are not equal", + args: args{ + a: &v2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: v2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: scaleTargetRef0, + MinReplicas: minReplicas0, + MaxReplicas: maxReplicas0, + Metrics: metrics0, + }, + }, + b: &v2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: v2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: scaleTargetRef0, + MinReplicas: minReplicas0, + MaxReplicas: maxReplicas1, + Metrics: metrics0, + }, + }, + }, + want: false, + }, + { + name: "HPA metrics are not equal", + args: args{ + a: &v2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: v2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: scaleTargetRef0, + MinReplicas: minReplicas0, + MaxReplicas: maxReplicas0, + Metrics: metrics0, + }, + }, + b: &v2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: name0, + Namespace: namespace0, + OwnerReferences: ownerReferences0, + }, + Spec: v2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: scaleTargetRef0, + MinReplicas: minReplicas0, + MaxReplicas: maxReplicas0, + Metrics: metrics1, + }, + }, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := hpaEqual(tt.args.a, tt.args.b); got != tt.want { + t.Errorf("hpaEqual() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_envEqual(t *testing.T) { + type args struct { + e1 []corev1.EnvVar + e2 []corev1.EnvVar + } + + var11 := corev1.EnvVar{ + Name: "var1", + Value: "var1", + } + var12 := corev1.EnvVar{ + Name: "var1", + Value: "var2", + } + + tests := []struct { + name string + args args + want bool + }{ + { + name: "envs equal, order equals", + args: args{ + e1: []corev1.EnvVar{var11, var12}, + e2: []corev1.EnvVar{var11, var12}, + }, + want: true, + }, + { + name: "envs equal, different order", + args: args{ + e1: []corev1.EnvVar{var11, var12}, + e2: []corev1.EnvVar{var12, var11}, + }, + want: true, + }, + { + name: "different length", + args: args{ + e1: []corev1.EnvVar{var11, var11}, + e2: []corev1.EnvVar{var11}, + }, + want: false, + }, + { + name: "envs different", + args: args{ + e1: []corev1.EnvVar{var11, var12}, + e2: []corev1.EnvVar{var11, var11}, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := envEqual(tt.args.e1, tt.args.e2); got != tt.want { + t.Errorf("envEqual() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/test/utils/integration/integration.go b/test/utils/integration/integration.go index 8df11839..5515a56e 100644 --- a/test/utils/integration/integration.go +++ b/test/utils/integration/integration.go @@ -5,23 +5,23 @@ import ( "context" "crypto/rand" "fmt" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "log" "path/filepath" "strings" "testing" "time" - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - "github.com/kyma-project/eventing-manager/pkg/subscriptionmanager" "github.com/kyma-project/eventing-manager/pkg/subscriptionmanager/manager" submanagermocks "github.com/kyma-project/eventing-manager/pkg/subscriptionmanager/manager/mocks" apiclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" - subscriptionmanagermocks "github.com/kyma-project/eventing-manager/pkg/subscriptionmanager/mocks" "github.com/stretchr/testify/mock" + subscriptionmanagermocks "github.com/kyma-project/eventing-manager/pkg/subscriptionmanager/mocks" + "github.com/kyma-project/eventing-manager/test" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -41,14 +41,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/metrics/server" "sigs.k8s.io/controller-runtime/pkg/webhook" - "github.com/kyma-project/eventing-manager/api/v1alpha1" - eventingctrl "github.com/kyma-project/eventing-manager/internal/controller/eventing" - "github.com/kyma-project/eventing-manager/options" - "github.com/kyma-project/eventing-manager/pkg/env" - "github.com/kyma-project/eventing-manager/pkg/eventing" - "github.com/kyma-project/eventing-manager/pkg/k8s" - "github.com/kyma-project/eventing-manager/pkg/logger" - evnttestutils "github.com/kyma-project/eventing-manager/test/utils" eventingv1alpha2 "github.com/kyma-project/kyma/components/eventing-controller/api/v1alpha2" natsv1alpha1 "github.com/kyma-project/nats-manager/api/v1alpha1" "github.com/kyma-project/nats-manager/testutils" @@ -58,6 +50,15 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + + "github.com/kyma-project/eventing-manager/api/v1alpha1" + eventingctrl "github.com/kyma-project/eventing-manager/internal/controller/eventing" + "github.com/kyma-project/eventing-manager/options" + "github.com/kyma-project/eventing-manager/pkg/env" + "github.com/kyma-project/eventing-manager/pkg/eventing" + "github.com/kyma-project/eventing-manager/pkg/k8s" + "github.com/kyma-project/eventing-manager/pkg/logger" + evnttestutils "github.com/kyma-project/eventing-manager/test/utils" ) const ( @@ -67,7 +68,7 @@ const ( testEnvStartAttempts = 10 BigPollingInterval = 3 * time.Second BigTimeOut = 120 * time.Second - SmallTimeOut = 6 * time.Second + SmallTimeOut = 60 * time.Second SmallPollingInterval = 1 * time.Second )