diff --git a/api/v1alpha1/eventing_types.go b/api/v1alpha1/eventing_types.go index cde3b654..3d317b46 100644 --- a/api/v1alpha1/eventing_types.go +++ b/api/v1alpha1/eventing_types.go @@ -171,6 +171,11 @@ type BackendConfig struct { // +kubebuilder:default:="sap.kyma.custom" // +kubebuilder:validation:XValidation:rule="self!=''", message="eventTypePrefix cannot be empty" EventTypePrefix string `json:"eventTypePrefix,omitempty"` + + // Domain defines the cluster public domain used to configure the EventMesh Subscriptions + // and their corresponding ApiRules. + // +kubebuilder:validation:Pattern:="^(?:([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])(\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]))*)?$" + Domain string `json:"domain,omitempty"` } // Publisher defines the configurations for eventing-publisher-proxy. diff --git a/config/crd/bases/operator.kyma-project.io_eventings.yaml b/config/crd/bases/operator.kyma-project.io_eventings.yaml index 852c69b7..3191763c 100644 --- a/config/crd/bases/operator.kyma-project.io_eventings.yaml +++ b/config/crd/bases/operator.kyma-project.io_eventings.yaml @@ -92,6 +92,12 @@ spec: natsStreamStorageType: File description: Config defines configuration for eventing backend. properties: + domain: + description: Domain defines the cluster public domain used + to configure the EventMesh Subscriptions and their corresponding + ApiRules. + pattern: ^(?:([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*)?$ + type: string eventMeshSecret: description: EventMeshSecret defines the namespaced name of K8s Secret containing EventMesh credentials. The format diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 66e8a51b..a9a3cb42 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -78,10 +78,6 @@ spec: value: "kyma-system" - name: NATS_URL value: eventing-nats.kyma-system.svc.cluster.local - - name: DOMAIN - value: kyma.example.com - - name: WEBHOOK_TOKEN_ENDPOINT - value: https://oauth2.kyma.example.com/oauth2/token - name: PUBLISHER_REQUESTS_CPU value: 10m - name: PUBLISHER_REQUESTS_MEMORY diff --git a/internal/controller/eventing/domain.go b/internal/controller/eventing/domain.go new file mode 100644 index 00000000..581e2e77 --- /dev/null +++ b/internal/controller/eventing/domain.go @@ -0,0 +1,37 @@ +package eventing + +import ( + "context" + "fmt" +) + +const ( + shootInfoConfigMapName = "shoot-info" + shootInfoConfigMapNamespace = "kube-system" + shootInfoConfigMapKeyDomain = "domain" + domainMissingMessageFormat = `domain configuration is missing. domain must be configured in either the Eventing` + + ` CustomResource under "Spec.Backend.Config.Domain" or in the ConfigMap "%s/%s" under "data.%s"` + domainMissingMessageFormatWithError = domainMissingMessageFormat + `: %v` +) + +func (r *Reconciler) readDomainFromConfigMap(ctx context.Context) (string, error) { + cm, err := r.kubeClient.GetConfigMap(ctx, shootInfoConfigMapName, shootInfoConfigMapNamespace) + if err != nil { + return "", err + } + return cm.Data[shootInfoConfigMapKeyDomain], nil +} + +func domainMissingError(err error) error { + if err != nil { + return fmt.Errorf( + domainMissingMessageFormatWithError, + shootInfoConfigMapNamespace, shootInfoConfigMapName, shootInfoConfigMapKeyDomain, err, + ) + } + + return fmt.Errorf( + domainMissingMessageFormat, + shootInfoConfigMapNamespace, shootInfoConfigMapName, shootInfoConfigMapKeyDomain, + ) +} diff --git a/internal/controller/eventing/domain_test.go b/internal/controller/eventing/domain_test.go new file mode 100644 index 00000000..e8f3581f --- /dev/null +++ b/internal/controller/eventing/domain_test.go @@ -0,0 +1,59 @@ +package eventing + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + + k8smocks "github.com/kyma-project/eventing-manager/pkg/k8s/mocks" + "github.com/kyma-project/eventing-manager/test/utils" +) + +func Test_readDomainFromConfigMap(t *testing.T) { + // given + ctx := context.TODO() + + cm := &corev1.ConfigMap{ + Data: map[string]string{ + shootInfoConfigMapKeyDomain: utils.Domain, + }, + } + + kubeClient := func() *k8smocks.Client { + kubeClient := new(k8smocks.Client) + kubeClient.On("GetConfigMap", ctx, shootInfoConfigMapName, shootInfoConfigMapNamespace). + Return(cm, nil).Once() + return kubeClient + } + + wantError := error(nil) + wantDomain := utils.Domain + + // when + r := &Reconciler{kubeClient: kubeClient()} + gotDomain, gotError := r.readDomainFromConfigMap(ctx) + + // then + assert.Equal(t, wantError, gotError) + assert.Equal(t, wantDomain, gotDomain) +} + +func Test_domainMissingError(t *testing.T) { + // given + const errorMessage = "some error" + err := fmt.Errorf(errorMessage) + + // when + err0 := domainMissingError(nil) + err1 := domainMissingError(err) + + // then + assert.NotNil(t, err0) + assert.NotNil(t, err1) + assert.False(t, strings.Contains(strings.ToLower(err0.Error()), "nil")) + assert.True(t, strings.Contains(err1.Error(), errorMessage)) +} diff --git a/internal/controller/eventing/eventmesh.go b/internal/controller/eventing/eventmesh.go index d083a384..f4662cec 100644 --- a/internal/controller/eventing/eventmesh.go +++ b/internal/controller/eventing/eventmesh.go @@ -5,20 +5,18 @@ import ( "context" "encoding/json" "fmt" - "github.com/kyma-project/eventing-manager/pkg/env" "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/api/v1alpha1" + "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" ) @@ -59,13 +57,31 @@ func (r *Reconciler) reconcileEventMeshSubManager(ctx context.Context, eventing return fmt.Errorf("failed to setup environment variables for EventMesh controller: %v", err) } + // Read the cluster domain from the Eventing CR, or + // read it from the configmap managed by gardener + domain := eventing.Spec.Backend.Config.Domain + if utils.IsEmpty(domain) { + r.namedLogger().Infof( + `Domain is not configured in the Eventing CR, reading it from the ConfigMap %s/%s`, + shootInfoConfigMapNamespace, shootInfoConfigMapName, + ) + domain, err = r.readDomainFromConfigMap(ctx) + if err != nil || utils.IsEmpty(domain) { + return domainMissingError(err) + } + } + r.namedLogger().Infof(`Domain is %s`, domain) + // get the subscription config defaultSubsConfig := r.getDefaultSubscriptionConfig() // get the subManager parameters eventMeshSubMgrParams := r.getEventMeshSubManagerParams() // get the hash of current config - specHash, err := r.getEventMeshBackendConfigHash(eventing.Spec.Backend.Config.EventMeshSecret, - eventing.Spec.Backend.Config.EventTypePrefix) + specHash, err := getEventMeshBackendConfigHash( + eventing.Spec.Backend.Config.EventMeshSecret, + eventing.Spec.Backend.Config.EventTypePrefix, + domain, + ) if err != nil { return err } @@ -80,7 +96,7 @@ func (r *Reconciler) reconcileEventMeshSubManager(ctx context.Context, eventing if r.eventMeshSubManager == nil { // create instance of EventMesh subscription manager - eventMeshSubManager, err := r.subManagerFactory.NewEventMeshManager() + eventMeshSubManager, err := r.subManagerFactory.NewEventMeshManager(domain) if err != nil { return err } diff --git a/internal/controller/eventing/eventmesh_test.go b/internal/controller/eventing/eventmesh_test.go index 13720e54..c24b47f2 100644 --- a/internal/controller/eventing/eventmesh_test.go +++ b/internal/controller/eventing/eventmesh_test.go @@ -41,6 +41,7 @@ func Test_reconcileEventMeshSubManager(t *testing.T) { utils.WithEventMeshBackend("test-namespace/test-secret-name"), utils.WithEventingPublisherData(2, 2, "199m", "99Mi", "399m", "199Mi"), utils.WithEventingEventTypePrefix("test-prefix"), + utils.WithEventingDomain(utils.Domain), ) givenOauthSecret := utils.NewOAuthSecret("eventing-webhook-auth", givenEventing.Namespace) @@ -48,6 +49,13 @@ func Test_reconcileEventMeshSubManager(t *testing.T) { givenBackendConfig := &env.BackendConfig{ EventingWebhookAuthSecretName: "eventing-webhook-auth", } + + givenConfigMap := &corev1.ConfigMap{ + Data: map[string]string{ + shootInfoConfigMapKeyDomain: utils.Domain, + }, + } + ctx := context.Background() // define test cases @@ -135,7 +143,7 @@ func Test_reconcileEventMeshSubManager(t *testing.T) { { name: "it should do nothing because subscription manager is already started", givenIsEventMeshSubManagerStarted: true, - givenHashBefore: int64(-8279197549452913403), + givenHashBefore: int64(4922936597877296700), givenEventMeshSubManagerMock: func() *submanagermocks.Manager { eventMeshSubManagerMock := new(submanagermocks.Manager) eventMeshSubManagerMock.On("Stop", mock.Anything).Return(nil).Once() @@ -151,12 +159,13 @@ func Test_reconcileEventMeshSubManager(t *testing.T) { }, givenKubeClientMock: func() k8s.Client { mockKubeClient := new(k8smocks.Client) + mockKubeClient.On("GetConfigMap", ctx, mock.Anything, mock.Anything).Return(givenConfigMap, nil).Once() mockKubeClient.On("PatchApply", ctx, mock.Anything).Return(nil).Once() mockKubeClient.On("GetSecret", ctx, mock.Anything, mock.Anything).Return( utils.NewEventMeshSecret("test-secret", givenEventing.Namespace), nil).Once() return mockKubeClient }, - wantHashAfter: int64(-8279197549452913403), + wantHashAfter: int64(4922936597877296700), }, { name: "it should initialize and start subscription manager because " + @@ -181,13 +190,14 @@ func Test_reconcileEventMeshSubManager(t *testing.T) { }, givenKubeClientMock: func() k8s.Client { mockKubeClient := new(k8smocks.Client) + mockKubeClient.On("GetConfigMap", ctx, mock.Anything, mock.Anything).Return(givenConfigMap, nil).Once() mockKubeClient.On("PatchApply", ctx, mock.Anything).Return(nil).Once() mockKubeClient.On("GetSecret", ctx, mock.Anything, mock.Anything).Return( utils.NewEventMeshSecret("test-secret", givenEventing.Namespace), nil).Once() return mockKubeClient }, wantAssertCheck: true, - wantHashAfter: int64(-8279197549452913403), + wantHashAfter: int64(4922936597877296700), }, { name: "it should retry to start subscription manager when subscription manager was " + @@ -252,7 +262,7 @@ func Test_reconcileEventMeshSubManager(t *testing.T) { return mockKubeClient }, wantAssertCheck: true, - wantHashAfter: int64(-8279197549452913403), + wantHashAfter: int64(4922936597877296700), }, } @@ -320,6 +330,136 @@ func Test_reconcileEventMeshSubManager(t *testing.T) { } } +func Test_reconcileEventMeshSubManager_ReadClusterDomain(t *testing.T) { + t.Parallel() + + const ( + namespace = "test-namespace" + ) + + ctx := context.Background() + + givenOauthSecret := utils.NewOAuthSecret("eventing-webhook-auth", namespace) + + givenBackendConfig := &env.BackendConfig{ + EventingWebhookAuthSecretName: "eventing-webhook-auth", + } + + givenConfigMap := &corev1.ConfigMap{ + Data: map[string]string{ + shootInfoConfigMapKeyDomain: utils.Domain, + }, + } + + testCases := []struct { + name string + givenEventing *v1alpha1.Eventing + givenEventMeshSubManagerMock func() *submanagermocks.Manager + givenEventingManagerMock func() *managermocks.Manager + givenManagerFactoryMock func(*submanagermocks.Manager) *subscriptionmanagermocks.ManagerFactory + givenKubeClientMock func() (k8s.Client, *k8smocks.Client) + }{ + { + name: "should not read the domain from the configmap because it is configured in the Eventing CR", + givenEventing: utils.NewEventingCR( + utils.WithEventingCRName("eventing"), + utils.WithEventingCRNamespace(namespace), + utils.WithEventMeshBackend("test-namespace/test-secret-name"), + utils.WithEventingEventTypePrefix("test-prefix"), + utils.WithEventingDomain(utils.Domain), + ), + givenEventMeshSubManagerMock: func() *submanagermocks.Manager { + eventMeshSubManagerMock := new(submanagermocks.Manager) + eventMeshSubManagerMock.On("Init", mock.Anything).Return(nil).Once() + return eventMeshSubManagerMock + }, + givenEventingManagerMock: func() *managermocks.Manager { + emMock := new(managermocks.Manager) + emMock.On("GetBackendConfig").Return(givenBackendConfig) + return emMock + }, + givenManagerFactoryMock: func(subManager *submanagermocks.Manager) *subscriptionmanagermocks.ManagerFactory { + subManagerFactoryMock := new(subscriptionmanagermocks.ManagerFactory) + subManagerFactoryMock.On("NewEventMeshManager", mock.Anything).Return(subManager, nil).Once() + return subManagerFactoryMock + }, + givenKubeClientMock: func() (k8s.Client, *k8smocks.Client) { + mockKubeClient := new(k8smocks.Client) + mockKubeClient.On("PatchApply", ctx, mock.Anything).Return(nil).Once() + mockKubeClient.On("GetSecret", ctx, mock.Anything, mock.Anything).Return( + utils.NewEventMeshSecret("test-secret", namespace), nil).Once() + return mockKubeClient, mockKubeClient + }, + }, + { + name: "should read the domain from the configmap because it is not configured in the Eventing CR", + givenEventing: utils.NewEventingCR( + utils.WithEventingCRName("eventing"), + utils.WithEventingCRNamespace(namespace), + utils.WithEventMeshBackend("test-namespace/test-secret-name"), + utils.WithEventingEventTypePrefix("test-prefix"), + utils.WithEventingDomain(""), + ), + givenEventMeshSubManagerMock: func() *submanagermocks.Manager { + eventMeshSubManagerMock := new(submanagermocks.Manager) + eventMeshSubManagerMock.On("Init", mock.Anything).Return(nil).Once() + return eventMeshSubManagerMock + }, + givenEventingManagerMock: func() *managermocks.Manager { + emMock := new(managermocks.Manager) + emMock.On("GetBackendConfig").Return(givenBackendConfig) + return emMock + }, + givenManagerFactoryMock: func(subManager *submanagermocks.Manager) *subscriptionmanagermocks.ManagerFactory { + subManagerFactoryMock := new(subscriptionmanagermocks.ManagerFactory) + subManagerFactoryMock.On("NewEventMeshManager", mock.Anything).Return(subManager, nil).Once() + return subManagerFactoryMock + }, + givenKubeClientMock: func() (k8s.Client, *k8smocks.Client) { + mockKubeClient := new(k8smocks.Client) + mockKubeClient.On("GetConfigMap", ctx, mock.Anything, mock.Anything).Return(givenConfigMap, nil).Once() + mockKubeClient.On("PatchApply", ctx, mock.Anything).Return(nil).Once() + mockKubeClient.On("GetSecret", ctx, mock.Anything, mock.Anything).Return( + utils.NewEventMeshSecret("test-secret", namespace), nil).Once() + return mockKubeClient, mockKubeClient + }, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + // given + testEnv := NewMockedUnitTestEnvironment(t, tc.givenEventing, givenOauthSecret) + testEnv.Reconciler.backendConfig = *givenBackendConfig + + givenEventMeshSubManagerMock := tc.givenEventMeshSubManagerMock() + givenManagerFactoryMock := tc.givenManagerFactoryMock(givenEventMeshSubManagerMock) + givenEventingManagerMock := tc.givenEventingManagerMock() + + var mockClient *k8smocks.Client + if tc.givenKubeClientMock != nil { + testEnv.Reconciler.kubeClient, mockClient = tc.givenKubeClientMock() + } + + testEnv.Reconciler.isEventMeshSubManagerStarted = true + testEnv.Reconciler.eventingManager = givenEventingManagerMock + testEnv.Reconciler.subManagerFactory = givenManagerFactoryMock + testEnv.Reconciler.eventMeshSubManager = nil + + // when + err := testEnv.Reconciler.reconcileEventMeshSubManager(ctx, tc.givenEventing) + + // then + require.NoError(t, err) + givenEventMeshSubManagerMock.AssertExpectations(t) + givenEventingManagerMock.AssertExpectations(t) + givenManagerFactoryMock.AssertExpectations(t) + mockClient.AssertExpectations(t) + }) + } +} + func Test_stopEventMeshSubManager(t *testing.T) { t.Parallel() diff --git a/internal/controller/eventing/integrationtests/controller/integration_test.go b/internal/controller/eventing/integrationtests/controller/integration_test.go index 86455aa0..56e353e7 100644 --- a/internal/controller/eventing/integrationtests/controller/integration_test.go +++ b/internal/controller/eventing/integrationtests/controller/integration_test.go @@ -95,6 +95,7 @@ func Test_CreateEventingCR_NATS(t *testing.T) { utils.WithEventingStreamData("Memory", "1M", 1, 1), utils.WithEventingPublisherData(2, 2, "199m", "99Mi", "399m", "199Mi"), utils.WithEventingEventTypePrefix("test-prefix"), + utils.WithEventingDomain(utils.Domain), ), givenNATS: natstestutils.NewNATSCR( natstestutils.WithNATSCRDefaults(), @@ -115,6 +116,7 @@ func Test_CreateEventingCR_NATS(t *testing.T) { utils.WithEventingCRMinimal(), utils.WithEventingStreamData("Memory", "1M", 1, 1), utils.WithEventingPublisherData(1, 1, "199m", "99Mi", "399m", "199Mi"), + utils.WithEventingDomain(utils.Domain), ), givenNATS: natstestutils.NewNATSCR( natstestutils.WithNATSCRDefaults(), @@ -517,6 +519,7 @@ func Test_CreateEventingCR_EventMesh(t *testing.T) { utils.WithEventMeshBackend("test-secret-name2"), utils.WithEventingPublisherData(2, 2, "199m", "99Mi", "399m", "199Mi"), utils.WithEventingEventTypePrefix("test-prefix"), + utils.WithEventingDomain(utils.Domain), ), givenDeploymentReady: true, wantMatches: gomega.And( @@ -533,6 +536,7 @@ func Test_CreateEventingCR_EventMesh(t *testing.T) { utils.WithEventMeshBackend("test-secret-name3"), utils.WithEventingPublisherData(2, 2, "199m", "99Mi", "399m", "199Mi"), utils.WithEventingEventTypePrefix("test-prefix"), + utils.WithEventingDomain(utils.Domain), ), wantMatches: gomega.And( matchers.HaveStatusProcessing(), @@ -619,6 +623,7 @@ func Test_DeleteEventingCR(t *testing.T) { utils.WithEventMeshBackend("test-secret-name"), utils.WithEventingPublisherData(1, 1, "199m", "99Mi", "399m", "199Mi"), utils.WithEventingEventTypePrefix("test-prefix"), + utils.WithEventingDomain(utils.Domain), ), }, { @@ -641,6 +646,7 @@ func Test_DeleteEventingCR(t *testing.T) { utils.WithEventMeshBackend("test-secret-name"), utils.WithEventingPublisherData(1, 1, "199m", "99Mi", "399m", "199Mi"), utils.WithEventingEventTypePrefix("test-prefix"), + utils.WithEventingDomain(utils.Domain), ), givenSubscription: utils.NewSubscription("test-eventmesh-subscription", "test-eventmesh-namespace"), wantMatches: gomega.And( @@ -868,6 +874,7 @@ func Test_WatcherNATSResource(t *testing.T) { utils.WithEventMeshBackend("test-secret-name"), utils.WithEventingPublisherData(1, 1, "199m", "99Mi", "399m", "199Mi"), utils.WithStatusState(tc.wantedOriginalEventingState), + utils.WithEventingDomain(utils.Domain), ) // create necessary EventMesh secrets testEnvironment.EnsureOAuthSecretCreated(t, eventingResource) @@ -879,6 +886,7 @@ func Test_WatcherNATSResource(t *testing.T) { utils.WithEventingStreamData("Memory", "1M", 1, 1), utils.WithEventingPublisherData(1, 1, "199m", "99Mi", "399m", "199Mi"), utils.WithStatusState(tc.wantedOriginalEventingState), + utils.WithEventingDomain(utils.Domain), ) } testEnvironment.EnsureK8sResourceCreated(t, eventingResource) diff --git a/internal/controller/eventing/integrationtests/controller_switching/integration_test.go b/internal/controller/eventing/integrationtests/controller_switching/integration_test.go index f338779d..d69dae7a 100644 --- a/internal/controller/eventing/integrationtests/controller_switching/integration_test.go +++ b/internal/controller/eventing/integrationtests/controller_switching/integration_test.go @@ -75,11 +75,13 @@ func Test_Switching(t *testing.T) { utils.WithEventingStreamData("Memory", "1M", 1, 1), utils.WithEventingPublisherData(2, 2, "199m", "99Mi", "399m", "199Mi"), utils.WithEventingEventTypePrefix("test-prefix"), + utils.WithEventingDomain(utils.Domain), ), givenSwitchedEventing: utils.NewEventingCR( utils.WithEventMeshBackend("test-secret-name2"), utils.WithEventingPublisherData(2, 2, "199m", "99Mi", "399m", "199Mi"), utils.WithEventingEventTypePrefix("test-prefix"), + utils.WithEventingDomain(utils.Domain), ), wantPreSwitchMatches: gomega.And( matchers.HaveStatusReady(), @@ -104,12 +106,14 @@ func Test_Switching(t *testing.T) { utils.WithEventMeshBackend("test-secret-name2"), utils.WithEventingPublisherData(2, 2, "199m", "99Mi", "399m", "199Mi"), utils.WithEventingEventTypePrefix("test-prefix"), + utils.WithEventingDomain(utils.Domain), ), givenSwitchedEventing: utils.NewEventingCR( utils.WithEventingCRMinimal(), utils.WithEventingStreamData("Memory", "1M", 1, 1), utils.WithEventingPublisherData(2, 2, "199m", "99Mi", "399m", "199Mi"), utils.WithEventingEventTypePrefix("test-prefix"), + utils.WithEventingDomain(utils.Domain), ), wantPreSwitchMatches: gomega.And( matchers.HaveStatusReady(), diff --git a/internal/controller/eventing/integrationtests/validation/integration_test.go b/internal/controller/eventing/integrationtests/validation/integration_test.go index a44a9ed4..dd59d746 100644 --- a/internal/controller/eventing/integrationtests/validation/integration_test.go +++ b/internal/controller/eventing/integrationtests/validation/integration_test.go @@ -48,6 +48,7 @@ const ( msgsPerTopic = 1000000 eventTypePrefix = "eventTypePrefix" eventMeshSecret = "eventMeshSecret" + domain = "domain" someSecret = "namespace/name" wrongSecret = "gibberish" publisher = "publisher" @@ -235,6 +236,192 @@ func Test_Validate_CreateEventing(t *testing.T) { }, }, }, + + // validate the spec.backend.config.domain + { + name: `validation of spec.backend.config.domain passes if domain is nil`, + givenUnstructuredEventing: unstructured.Unstructured{ + Object: map[string]any{ + kind: kindEventing, + apiVersion: apiVersionEventing, + metadata: map[string]any{ + name: test.GetRandK8sName(7), + namespace: test.GetRandK8sName(7), + }, + spec: map[string]any{ + backend: map[string]any{ + config: map[string]any{}, + }, + }, + }, + }, + }, + { + name: `validation of spec.backend.config.domain passes if domain is empty`, + givenUnstructuredEventing: unstructured.Unstructured{ + Object: map[string]any{ + kind: kindEventing, + apiVersion: apiVersionEventing, + metadata: map[string]any{ + name: test.GetRandK8sName(7), + namespace: test.GetRandK8sName(7), + }, + spec: map[string]any{ + backend: map[string]any{ + config: map[string]any{ + domain: "", + }, + }, + }, + }, + }, + }, + { + name: `validation of spec.backend.config.domain passes if domain is valid`, + givenUnstructuredEventing: unstructured.Unstructured{ + Object: map[string]any{ + kind: kindEventing, + apiVersion: apiVersionEventing, + metadata: map[string]any{ + name: test.GetRandK8sName(7), + namespace: test.GetRandK8sName(7), + }, + spec: map[string]any{ + backend: map[string]any{ + config: map[string]any{ + domain: "domain.com", + }, + }, + }, + }, + }, + }, + { + name: `validation of spec.backend.config.domain passes if domain is valid`, + givenUnstructuredEventing: unstructured.Unstructured{ + Object: map[string]any{ + kind: kindEventing, + apiVersion: apiVersionEventing, + metadata: map[string]any{ + name: test.GetRandK8sName(7), + namespace: test.GetRandK8sName(7), + }, + spec: map[string]any{ + backend: map[string]any{ + config: map[string]any{ + domain: "domain.part1.part2.part3.part4", + }, + }, + }, + }, + }, + }, + { + name: `validation of spec.backend.config.domain fails if domain is invalid`, + givenUnstructuredEventing: unstructured.Unstructured{ + Object: map[string]any{ + kind: kindEventing, + apiVersion: apiVersionEventing, + metadata: map[string]any{ + name: test.GetRandK8sName(7), + namespace: test.GetRandK8sName(7), + }, + spec: map[string]any{ + backend: map[string]any{ + config: map[string]any{ + domain: " ", + }, + }, + }, + }, + }, + wantErrMsg: `spec.backend.config.domain: Invalid value: " ": spec.backend.config.domain in body should match '^(?:([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*)?$'`, + }, + { + name: `validation of spec.backend.config.domain fails if domain is invalid`, + givenUnstructuredEventing: unstructured.Unstructured{ + Object: map[string]any{ + kind: kindEventing, + apiVersion: apiVersionEventing, + metadata: map[string]any{ + name: test.GetRandK8sName(7), + namespace: test.GetRandK8sName(7), + }, + spec: map[string]any{ + backend: map[string]any{ + config: map[string]any{ + domain: "http://domain.com", + }, + }, + }, + }, + }, + wantErrMsg: `spec.backend.config.domain: Invalid value: "http://domain.com": spec.backend.config.domain in body should match '^(?:([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*)?$'`, + }, + { + name: `validation of spec.backend.config.domain fails if domain is invalid`, + givenUnstructuredEventing: unstructured.Unstructured{ + Object: map[string]any{ + kind: kindEventing, + apiVersion: apiVersionEventing, + metadata: map[string]any{ + name: test.GetRandK8sName(7), + namespace: test.GetRandK8sName(7), + }, + spec: map[string]any{ + backend: map[string]any{ + config: map[string]any{ + domain: "https://domain.com", + }, + }, + }, + }, + }, + wantErrMsg: `spec.backend.config.domain: Invalid value: "https://domain.com": spec.backend.config.domain in body should match '^(?:([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*)?$'`, + }, + { + name: `validation of spec.backend.config.domain fails if domain is invalid`, + givenUnstructuredEventing: unstructured.Unstructured{ + Object: map[string]any{ + kind: kindEventing, + apiVersion: apiVersionEventing, + metadata: map[string]any{ + name: test.GetRandK8sName(7), + namespace: test.GetRandK8sName(7), + }, + spec: map[string]any{ + backend: map[string]any{ + config: map[string]any{ + domain: "domain.com:8080", + }, + }, + }, + }, + }, + wantErrMsg: `spec.backend.config.domain: Invalid value: "domain.com:8080": spec.backend.config.domain in body should match '^(?:([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*)?$'`, + }, + { + name: `validation of spec.backend.config.domain fails if domain is invalid`, + givenUnstructuredEventing: unstructured.Unstructured{ + Object: map[string]any{ + kind: kindEventing, + apiVersion: apiVersionEventing, + metadata: map[string]any{ + name: test.GetRandK8sName(7), + namespace: test.GetRandK8sName(7), + }, + spec: map[string]any{ + backend: map[string]any{ + config: map[string]any{ + domain: "domain.com/endpoint", + }, + }, + }, + }, + }, + wantErrMsg: `spec.backend.config.domain: Invalid value: "domain.com/endpoint": spec.backend.config.domain in body should match '^(?:([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*)?$'`, + }, + { name: `validation of spec.backend.type fails for values other than NATS or EventMesh`, givenUnstructuredEventing: unstructured.Unstructured{ diff --git a/internal/controller/eventing/utils.go b/internal/controller/eventing/utils.go index cfe1fe02..b895581b 100644 --- a/internal/controller/eventing/utils.go +++ b/internal/controller/eventing/utils.go @@ -2,11 +2,11 @@ package eventing import ( "context" + "fmt" eventingv1alpha1 "github.com/kyma-project/eventing-manager/api/v1alpha1" "github.com/kyma-project/eventing-manager/pkg/env" "github.com/mitchellh/hashstructure/v2" - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) @@ -44,9 +44,8 @@ func (r *Reconciler) getNATSBackendConfigHash(defaultSubscriptionConfig env.Defa return int64(hash), nil } -func (r *Reconciler) getEventMeshBackendConfigHash(eventMeshSecret, eventTypePrefix string) (int64, error) { - eventMeshBackendConfig := eventMeshSecret + eventTypePrefix - +func getEventMeshBackendConfigHash(eventMeshSecret, eventTypePrefix, domain string) (int64, error) { + eventMeshBackendConfig := fmt.Sprintf("[%s][%s][%s]", eventMeshSecret, eventTypePrefix, domain) hash, err := hashstructure.Hash(eventMeshBackendConfig, hashstructure.FormatV2, nil) if err != nil { return 0, err diff --git a/internal/controller/eventing/utils_test.go b/internal/controller/eventing/utils_test.go index 6e9ee238..38f49b12 100644 --- a/internal/controller/eventing/utils_test.go +++ b/internal/controller/eventing/utils_test.go @@ -4,6 +4,8 @@ import ( "context" "testing" + "github.com/stretchr/testify/assert" + eventingv1alpha1 "github.com/kyma-project/eventing-manager/api/v1alpha1" "github.com/kyma-project/eventing-manager/test/utils" "github.com/stretchr/testify/require" @@ -89,3 +91,20 @@ func Test_removeFinalizer(t *testing.T) { require.False(t, reconciler.containsFinalizer(&gotEventing)) }) } + +func TestReconciler_getEventMeshBackendConfigHash(t *testing.T) { + hash, err := getEventMeshBackendConfigHash("kyma-system/eventing-backend", "sap.kyma.custom", "domain.com") + assert.NoError(t, err) + assert.NotEqual(t, 0, hash) +} + +func TestReconciler_getEventMeshBackendConfigHash_EnsureConsistencyAndUniqueness(t *testing.T) { + hash1, err1 := getEventMeshBackendConfigHash("kyma-system/eventing-backend", "sap.kyma.custom", "domain.com") + hash2, err2 := getEventMeshBackendConfigHash("kyma-system/eventing-backend", "sap.kyma.custom", "domain.com") + hash3, err3 := getEventMeshBackendConfigHash("kyma-system/eventing-backen", "dsap.kyma.cust", "omdomain.com") + assert.NoError(t, err1) + assert.NoError(t, err2) + assert.NoError(t, err3) + assert.Equal(t, hash1, hash2) + assert.NotEqual(t, hash1, hash3) +} diff --git a/internal/controller/subscription/eventmesh/reconciler.go b/internal/controller/subscription/eventmesh/reconciler.go index be6408e0..8649f310 100644 --- a/internal/controller/subscription/eventmesh/reconciler.go +++ b/internal/controller/subscription/eventmesh/reconciler.go @@ -78,7 +78,7 @@ const ( func NewReconciler(ctx context.Context, client client.Client, logger *logger.Logger, recorder record.EventRecorder, cfg env.Config, cleaner cleaner.Cleaner, eventMeshBackend eventmesh.Backend, credential *eventmesh.OAuth2ClientCredentials, mapper backendutils.NameMapper, validator sink.Validator, - collector *metrics.Collector) *Reconciler { + collector *metrics.Collector, domain string) *Reconciler { if err := eventMeshBackend.Initialize(cfg); err != nil { logger.WithContext().Errorw("Failed to start reconciler", "name", reconcilerName, "error", err) @@ -90,7 +90,7 @@ func NewReconciler(ctx context.Context, client client.Client, logger *logger.Log logger: logger, recorder: recorder, Backend: eventMeshBackend, - Domain: cfg.Domain, + Domain: domain, cleaner: cleaner, oauth2credentials: credential, nameMapper: mapper, diff --git a/internal/controller/subscription/eventmesh/reconciler_internal_integration_test.go b/internal/controller/subscription/eventmesh/reconciler_internal_integration_test.go index 0df24d43..a3bff798 100644 --- a/internal/controller/subscription/eventmesh/reconciler_internal_integration_test.go +++ b/internal/controller/subscription/eventmesh/reconciler_internal_integration_test.go @@ -33,14 +33,11 @@ import ( "github.com/kyma-project/eventing-manager/pkg/featureflags" eventinglogger "github.com/kyma-project/eventing-manager/pkg/logger" "github.com/kyma-project/eventing-manager/pkg/object" + "github.com/kyma-project/eventing-manager/test/utils" reconcilertesting "github.com/kyma-project/eventing-manager/testing" eventingv1alpha2 "github.com/kyma-project/kyma/components/eventing-controller/api/v1alpha2" ) -const ( - domain = "domain.com" -) - // TestReconciler_Reconcile tests the return values of the Reconcile() method of the reconciler. // This is important, as it dictates whether the reconciliation should be requeued by Controller Runtime, // and if so with how much initial delay. Returning error or a `Result{Requeue: true}` would cause the reconciliation to be requeued. @@ -107,7 +104,8 @@ func TestReconciler_Reconcile(t *testing.T) { te.credentials, te.mapper, happyValidator, - col) + col, + utils.Domain) }, wantReconcileResult: ctrl.Result{}, wantReconcileError: nil, @@ -128,7 +126,8 @@ func TestReconciler_Reconcile(t *testing.T) { te.credentials, te.mapper, unhappyValidator, - col) + col, + utils.Domain) }, wantReconcileResult: ctrl.Result{}, wantReconcileError: nil, @@ -150,7 +149,8 @@ func TestReconciler_Reconcile(t *testing.T) { te.credentials, te.mapper, happyValidator, - col) + col, + utils.Domain) }, wantReconcileResult: ctrl.Result{}, wantReconcileError: backendSyncErr, @@ -172,7 +172,8 @@ func TestReconciler_Reconcile(t *testing.T) { te.credentials, te.mapper, happyValidator, - col) + col, + utils.Domain) }, wantReconcileResult: ctrl.Result{}, wantReconcileError: backendDeleteErr, @@ -193,7 +194,8 @@ func TestReconciler_Reconcile(t *testing.T) { te.credentials, te.mapper, unhappyValidator, - col) + col, + utils.Domain) }, wantReconcileResult: ctrl.Result{}, wantReconcileError: validatorErr, @@ -215,7 +217,8 @@ func TestReconciler_Reconcile(t *testing.T) { te.credentials, te.mapper, happyValidator, - col) + col, + utils.Domain) }, wantReconcileResult: ctrl.Result{ RequeueAfter: requeueAfterDuration, @@ -287,7 +290,8 @@ func TestReconciler_APIRuleConfig(t *testing.T) { te.credentials, te.mapper, validator, - col), + col, + utils.Domain), te.fakeClient }, givenEventingWebhookAuthEnabled: false, @@ -316,7 +320,8 @@ func TestReconciler_APIRuleConfig(t *testing.T) { te.credentials, te.mapper, validator, - col), + col, + utils.Domain), te.fakeClient }, givenEventingWebhookAuthEnabled: true, @@ -415,7 +420,8 @@ func TestReconciler_APIRuleConfig_Upgrade(t *testing.T) { te.credentials, te.mapper, validator, - col), + col, + utils.Domain), te.fakeClient }, givenEventingWebhookAuthEnabled: false, @@ -450,7 +456,8 @@ func TestReconciler_APIRuleConfig_Upgrade(t *testing.T) { te.credentials, te.mapper, validator, - col), + col, + utils.Domain), te.fakeClient }, givenEventingWebhookAuthEnabled: true, @@ -606,7 +613,7 @@ func TestReconciler_PreserveBackendHashes(t *testing.T) { te.backend.On("Initialize", mock.Anything).Return(nil) te.backend.On("SyncSubscription", mock.Anything, mock.Anything, mock.Anything).Return(true, nil) return NewReconciler(ctx, te.fakeClient, te.logger, te.recorder, te.cfg, te.cleaner, - te.backend, te.credentials, te.mapper, validator, collector), te.fakeClient + te.backend, te.credentials, te.mapper, validator, collector, utils.Domain), te.fakeClient }, wantEv2Hash: ev2hash, wantEventMeshHash: eventMeshHash, @@ -633,7 +640,7 @@ func TestReconciler_PreserveBackendHashes(t *testing.T) { te.backend.On("Initialize", mock.Anything).Return(nil) te.backend.On("SyncSubscription", mock.Anything, mock.Anything, mock.Anything).Return(true, nil) return NewReconciler(ctx, te.fakeClient, te.logger, te.recorder, te.cfg, te.cleaner, - te.backend, te.credentials, te.mapper, validator, collector), te.fakeClient + te.backend, te.credentials, te.mapper, validator, collector, utils.Domain), te.fakeClient }, wantEv2Hash: ev2hash, wantEventMeshHash: eventMeshHash, @@ -885,7 +892,7 @@ func Test_syncConditionSubscribed(t *testing.T) { r := Reconciler{ nameMapper: backendutils.NewBEBSubscriptionNameMapper( - domain, + utils.Domain, eventmesh.MaxSubscriptionNameLength, ), syncConditionWebhookCallStatus: syncConditionWebhookCallStatus, @@ -989,7 +996,7 @@ func Test_syncConditionSubscriptionActive(t *testing.T) { } r := Reconciler{ - nameMapper: backendutils.NewBEBSubscriptionNameMapper(domain, eventmesh.MaxSubscriptionNameLength), + nameMapper: backendutils.NewBEBSubscriptionNameMapper(utils.Domain, eventmesh.MaxSubscriptionNameLength), logger: logger, } @@ -1375,7 +1382,7 @@ func setupTestEnvironment(t *testing.T, objs ...client.Object) *testEnvironment } emptyConfig := env.Config{} credentials := &eventmesh.OAuth2ClientCredentials{} - nameMapper := backendutils.NewBEBSubscriptionNameMapper(domain, eventmesh.MaxSubscriptionNameLength) + nameMapper := backendutils.NewBEBSubscriptionNameMapper(utils.Domain, eventmesh.MaxSubscriptionNameLength) eventMeshCleaner := cleaner.NewEventMeshCleaner(nil) return &testEnvironment{ diff --git a/internal/controller/subscription/eventmesh/test/utils.go b/internal/controller/subscription/eventmesh/test/utils.go index 52a3eeb7..2fd3c9f7 100644 --- a/internal/controller/subscription/eventmesh/test/utils.go +++ b/internal/controller/subscription/eventmesh/test/utils.go @@ -45,6 +45,7 @@ import ( "github.com/kyma-project/eventing-manager/pkg/featureflags" "github.com/kyma-project/eventing-manager/pkg/logger" "github.com/kyma-project/eventing-manager/pkg/utils" + testutils "github.com/kyma-project/eventing-manager/test/utils" reconcilertesting "github.com/kyma-project/eventing-manager/testing" eventingv1alpha2 "github.com/kyma-project/kyma/components/eventing-controller/api/v1alpha2" ) @@ -67,7 +68,6 @@ const ( bigTimeOut = 40 * time.Second smallTimeOut = 5 * time.Second smallPollingInterval = 1 * time.Second - domain = "domain.com" namespacePrefixLength = 5 syncPeriodSeconds = 2 maxReconnects = 10 @@ -130,7 +130,7 @@ func setupSuite() error { } // setup nameMapper for EventMesh - emTestEnsemble.nameMapper = backendutils.NewBEBSubscriptionNameMapper(domain, + emTestEnsemble.nameMapper = backendutils.NewBEBSubscriptionNameMapper(testutils.Domain, backendeventmesh.MaxSubscriptionNameLength) // setup eventMesh reconciler @@ -156,6 +156,7 @@ func setupSuite() error { emTestEnsemble.nameMapper, sinkValidator, col, + testutils.Domain, ) if err = testReconciler.SetupUnmanaged(k8sManager); err != nil { @@ -240,8 +241,6 @@ func getEnvConfig() env.Config { ClientSecret: "foo-secret", TokenEndpoint: emTestEnsemble.eventMeshMock.TokenURL, WebhookActivationTimeout: 0, - WebhookTokenEndpoint: "foo-token-endpoint", - Domain: domain, EventTypePrefix: reconcilertesting.EventMeshPrefix, BEBNamespace: reconcilertesting.EventMeshNamespaceNS, Qos: string(eventMeshtypes.QosAtLeastOnce), diff --git a/internal/controller/subscription/eventmesh/testwebhookauth/utils.go b/internal/controller/subscription/eventmesh/testwebhookauth/utils.go index ced6e678..487e4493 100644 --- a/internal/controller/subscription/eventmesh/testwebhookauth/utils.go +++ b/internal/controller/subscription/eventmesh/testwebhookauth/utils.go @@ -40,6 +40,7 @@ import ( "github.com/kyma-project/eventing-manager/pkg/featureflags" "github.com/kyma-project/eventing-manager/pkg/logger" "github.com/kyma-project/eventing-manager/pkg/utils" + testutils "github.com/kyma-project/eventing-manager/test/utils" reconcilertesting "github.com/kyma-project/eventing-manager/testing" eventingv1alpha2 "github.com/kyma-project/kyma/components/eventing-controller/api/v1alpha2" ) @@ -60,7 +61,6 @@ const ( twoMinTimeOut = 120 * time.Second bigPollingInterval = 3 * time.Second bigTimeOut = 40 * time.Second - domain = "domain.com" namespacePrefixLength = 5 syncPeriodSeconds = 2 maxReconnects = 10 @@ -131,7 +131,7 @@ func setupSuite() error { } // setup nameMapper for EventMesh - emTestEnsemble.nameMapper = backendutils.NewBEBSubscriptionNameMapper(domain, + emTestEnsemble.nameMapper = backendutils.NewBEBSubscriptionNameMapper(testutils.Domain, backendeventmesh.MaxSubscriptionNameLength) // setup eventMesh reconciler @@ -152,6 +152,7 @@ func setupSuite() error { emTestEnsemble.nameMapper, sinkValidator, col, + testutils.Domain, ) if err = testReconciler.SetupUnmanaged(k8sManager); err != nil { @@ -235,8 +236,6 @@ func getEnvConfig() env.Config { ClientSecret: "foo-secret", TokenEndpoint: emTestEnsemble.eventMeshMock.TokenURL, WebhookActivationTimeout: 0, - WebhookTokenEndpoint: "foo-token-endpoint", - Domain: domain, EventTypePrefix: reconcilertesting.EventMeshPrefix, BEBNamespace: reconcilertesting.EventMeshNamespaceNS, Qos: string(eventmeshtypes.QosAtLeastOnce), diff --git a/internal/controller/subscription/eventmesh/testwithory/utils.go b/internal/controller/subscription/eventmesh/testwithory/utils.go index 93ef950b..d98fe366 100644 --- a/internal/controller/subscription/eventmesh/testwithory/utils.go +++ b/internal/controller/subscription/eventmesh/testwithory/utils.go @@ -45,6 +45,7 @@ import ( "github.com/kyma-project/eventing-manager/pkg/featureflags" "github.com/kyma-project/eventing-manager/pkg/logger" "github.com/kyma-project/eventing-manager/pkg/utils" + testutils "github.com/kyma-project/eventing-manager/test/utils" reconcilertesting "github.com/kyma-project/eventing-manager/testing" eventingv1alpha2 "github.com/kyma-project/kyma/components/eventing-controller/api/v1alpha2" ) @@ -154,6 +155,7 @@ func setupSuite() error { emTestEnsemble.nameMapper, sinkValidator, col, + testutils.Domain, ) if err = testReconciler.SetupUnmanaged(k8sManager); err != nil { @@ -238,8 +240,6 @@ func getEnvConfig() env.Config { ClientSecret: "foo-secret", TokenEndpoint: emTestEnsemble.eventMeshMock.TokenURL, WebhookActivationTimeout: 0, - WebhookTokenEndpoint: "foo-token-endpoint", - Domain: domain, EventTypePrefix: reconcilertesting.EventMeshPrefix, BEBNamespace: reconcilertesting.EventMeshNamespaceNS, Qos: string(eventMeshtypes.QosAtLeastOnce), diff --git a/pkg/backend/eventmesh/eventmesh_integration_test.go b/pkg/backend/eventmesh/eventmesh_integration_test.go index 66ecbeb9..7ff656e7 100644 --- a/pkg/backend/eventmesh/eventmesh_integration_test.go +++ b/pkg/backend/eventmesh/eventmesh_integration_test.go @@ -595,8 +595,6 @@ func Test_SyncSubscription(t *testing.T) { ClientSecret: "client-secret", TokenEndpoint: eventMeshMock.TokenURL, WebhookActivationTimeout: 0, - WebhookTokenEndpoint: "webhook-token-endpoint", - Domain: "domain.com", EventTypePrefix: controllertesting.EventTypePrefix, BEBNamespace: "/default/ns", Qos: string(types.QosAtLeastOnce), diff --git a/pkg/env/config.go b/pkg/env/config.go index b9ef3c99..f2252f52 100644 --- a/pkg/env/config.go +++ b/pkg/env/config.go @@ -42,7 +42,6 @@ type Config struct { // Following details are for BEB to communicate to Kyma WebhookActivationTimeout time.Duration `envconfig:"WEBHOOK_ACTIVATION_TIMEOUT" default:"60s"` - WebhookTokenEndpoint string `envconfig:"WEBHOOK_TOKEN_ENDPOINT" required:"true"` // Default protocol setting for BEB ExemptHandshake bool `envconfig:"EXEMPT_HANDSHAKE" default:"true"` @@ -52,9 +51,6 @@ type Config struct { // Default namespace for BEB BEBNamespace string `envconfig:"BEB_NAMESPACE" default:"ns"` - // Domain holds the Kyma domain - Domain string `envconfig:"DOMAIN" required:"true"` - // EventTypePrefix prefix for the EventType // note: eventType format is ... EventTypePrefix string `envconfig:"EVENT_TYPE_PREFIX" required:"true"` diff --git a/pkg/env/config_test.go b/pkg/env/config_test.go index 9affb3f9..93086f5d 100644 --- a/pkg/env/config_test.go +++ b/pkg/env/config_test.go @@ -11,12 +11,10 @@ func Test_GetConfig(t *testing.T) { g := NewGomegaWithT(t) envs := map[string]string{ // required - "CLIENT_ID": "CLIENT_ID", - "CLIENT_SECRET": "CLIENT_SECRET", - "TOKEN_ENDPOINT": "TOKEN_ENDPOINT", - "WEBHOOK_TOKEN_ENDPOINT": "WEBHOOK_TOKEN_ENDPOINT", - "DOMAIN": "DOMAIN", - "EVENT_TYPE_PREFIX": "EVENT_TYPE_PREFIX", + "CLIENT_ID": "CLIENT_ID", + "CLIENT_SECRET": "CLIENT_SECRET", + "TOKEN_ENDPOINT": "TOKEN_ENDPOINT", + "EVENT_TYPE_PREFIX": "EVENT_TYPE_PREFIX", // optional "BEB_API_URL": "BEB_API_URL", "BEB_NAMESPACE": "/test", @@ -31,8 +29,6 @@ func Test_GetConfig(t *testing.T) { g.Expect(config.ClientID).To(Equal(envs["CLIENT_ID"])) g.Expect(config.ClientSecret).To(Equal(envs["CLIENT_SECRET"])) g.Expect(config.TokenEndpoint).To(Equal(envs["TOKEN_ENDPOINT"])) - g.Expect(config.WebhookTokenEndpoint).To(Equal(envs["WEBHOOK_TOKEN_ENDPOINT"])) - g.Expect(config.Domain).To(Equal(envs["DOMAIN"])) g.Expect(config.EventTypePrefix).To(Equal(envs["EVENT_TYPE_PREFIX"])) g.Expect(config.BEBNamespace).To(Equal(envs["BEB_NAMESPACE"])) // Ensure optional variables can be set diff --git a/pkg/k8s/client.go b/pkg/k8s/client.go index 9a0f99d0..7e6d567d 100644 --- a/pkg/k8s/client.go +++ b/pkg/k8s/client.go @@ -47,6 +47,7 @@ type Client interface { GetCRD(context.Context, string) (*apiextensionsv1.CustomResourceDefinition, error) ApplicationCRDExists(context.Context) (bool, error) GetSubscriptions(ctx context.Context) (*eventingv1alpha2.SubscriptionList, error) + GetConfigMap(ctx context.Context, name, namespace string) (*corev1.ConfigMap, error) } type KubeClient struct { @@ -216,3 +217,13 @@ func (c *KubeClient) GetSubscriptions(ctx context.Context) (*eventingv1alpha2.Su } return subscriptions, nil } + +// GetConfigMap returns a ConfigMap based on the given name and namespace. +func (c *KubeClient) GetConfigMap(ctx context.Context, name, namespace string) (*corev1.ConfigMap, error) { + cm := &corev1.ConfigMap{} + key := client.ObjectKey{Name: name, Namespace: namespace} + if err := c.client.Get(ctx, key, cm); err != nil { + return nil, err + } + return cm, nil +} diff --git a/pkg/k8s/client_test.go b/pkg/k8s/client_test.go index 1608d99b..441646b6 100644 --- a/pkg/k8s/client_test.go +++ b/pkg/k8s/client_test.go @@ -703,3 +703,54 @@ func TestGetSubscriptions(t *testing.T) { }) } } + +func Test_GetConfigMap(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + givenName string + givenNamespace string + wantNotFoundError bool + }{ + { + name: "should return configmap", + givenName: "test-name", + givenNamespace: "test-namespace", + wantNotFoundError: false, + }, + { + name: "should not return configmap", + givenName: "non-existing", + givenNamespace: "non-existing", + wantNotFoundError: true, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + // given + ctx := context.Background() + kubeClient := &KubeClient{client: fake.NewClientBuilder().Build()} + givenCM := testutils.NewConfigMap(tc.givenName, tc.givenNamespace) + if !tc.wantNotFoundError { + require.NoError(t, kubeClient.client.Create(ctx, givenCM)) + } + + // when + gotCM, err := kubeClient.GetConfigMap(context.Background(), tc.givenName, tc.givenNamespace) + + // then + if tc.wantNotFoundError { + require.Error(t, err) + require.True(t, apierrors.IsNotFound(err)) + } else { + require.NoError(t, err) + require.Equal(t, givenCM.GetName(), gotCM.Name) + } + }) + } +} diff --git a/pkg/k8s/mocks/client.go b/pkg/k8s/mocks/client.go index 7209202f..dd6bb785 100644 --- a/pkg/k8s/mocks/client.go +++ b/pkg/k8s/mocks/client.go @@ -273,6 +273,62 @@ func (_c *Client_GetCRD_Call) RunAndReturn(run func(context.Context, string) (*v return _c } +// GetConfigMap provides a mock function with given fields: ctx, name, namespace +func (_m *Client) GetConfigMap(ctx context.Context, name string, namespace string) (*corev1.ConfigMap, error) { + ret := _m.Called(ctx, name, namespace) + + var r0 *corev1.ConfigMap + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) (*corev1.ConfigMap, error)); ok { + return rf(ctx, name, namespace) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string) *corev1.ConfigMap); ok { + r0 = rf(ctx, name, namespace) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*corev1.ConfigMap) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, name, namespace) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Client_GetConfigMap_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetConfigMap' +type Client_GetConfigMap_Call struct { + *mock.Call +} + +// GetConfigMap is a helper method to define mock.On call +// - ctx context.Context +// - name string +// - namespace string +func (_e *Client_Expecter) GetConfigMap(ctx interface{}, name interface{}, namespace interface{}) *Client_GetConfigMap_Call { + return &Client_GetConfigMap_Call{Call: _e.mock.On("GetConfigMap", ctx, name, namespace)} +} + +func (_c *Client_GetConfigMap_Call) Run(run func(ctx context.Context, name string, namespace string)) *Client_GetConfigMap_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string)) + }) + return _c +} + +func (_c *Client_GetConfigMap_Call) Return(_a0 *corev1.ConfigMap, _a1 error) *Client_GetConfigMap_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Client_GetConfigMap_Call) RunAndReturn(run func(context.Context, string, string) (*corev1.ConfigMap, error)) *Client_GetConfigMap_Call { + _c.Call.Return(run) + return _c +} + // GetDeployment provides a mock function with given fields: _a0, _a1, _a2 func (_m *Client) GetDeployment(_a0 context.Context, _a1 string, _a2 string) (*appsv1.Deployment, error) { ret := _m.Called(_a0, _a1, _a2) diff --git a/pkg/subscriptionmanager/eventmesh/eventmesh.go b/pkg/subscriptionmanager/eventmesh/eventmesh.go index 8b2cacf9..772bfc43 100644 --- a/pkg/subscriptionmanager/eventmesh/eventmesh.go +++ b/pkg/subscriptionmanager/eventmesh/eventmesh.go @@ -73,12 +73,13 @@ type SubscriptionManager struct { eventMeshBackend backendeventmesh.Backend logger *logger.Logger collector *metrics.Collector + domain string } // NewSubscriptionManager creates the SubscriptionManager for BEB and initializes it as far as it // does not depend on non-common options. func NewSubscriptionManager(restCfg *rest.Config, metricsAddr string, resyncPeriod time.Duration, logger *logger.Logger, - collector *metrics.Collector) *SubscriptionManager { + collector *metrics.Collector, domain string) *SubscriptionManager { return &SubscriptionManager{ envCfg: env.GetConfig(), restCfg: restCfg, @@ -86,13 +87,14 @@ func NewSubscriptionManager(restCfg *rest.Config, metricsAddr string, resyncPeri resyncPeriod: resyncPeriod, logger: logger, collector: collector, + domain: domain, } } // Init implements the subscriptionmanager.Manager interface. func (c *SubscriptionManager) Init(mgr manager.Manager) error { - if len(c.envCfg.Domain) == 0 { - return fmt.Errorf("env var DOMAIN must be a non-empty value") + if len(c.domain) == 0 { + return fmt.Errorf("domain must be a non-empty value") } c.mgr = mgr return nil @@ -111,10 +113,10 @@ func (c *SubscriptionManager) Start(_ env.DefaultSubscriptionConfig, params subs // Need to read env to read BEB related secrets c.envCfg = env.GetConfig() - nameMapper := backendutils.NewBEBSubscriptionNameMapper(strings.TrimSpace(c.envCfg.Domain), + nameMapper := backendutils.NewBEBSubscriptionNameMapper(strings.TrimSpace(c.domain), backendeventmesh.MaxSubscriptionNameLength) ctrl.Log.WithName("BEB-subscription-manager").Info("using BEB name mapper", - "domainName", c.envCfg.Domain, + "domainName", c.domain, "maxNameLength", backendeventmesh.MaxSubscriptionNameLength) client := c.mgr.GetClient() @@ -139,6 +141,7 @@ func (c *SubscriptionManager) Start(_ env.DefaultSubscriptionConfig, params subs nameMapper, sink.NewValidator(ctx, client, recorder), c.collector, + c.domain, ) c.eventMeshBackend = eventMeshReconciler.Backend if err := eventMeshReconciler.SetupUnmanaged(c.mgr); err != nil { diff --git a/pkg/subscriptionmanager/eventmesh/eventmesh_test.go b/pkg/subscriptionmanager/eventmesh/eventmesh_test.go index 4e131705..259a9256 100644 --- a/pkg/subscriptionmanager/eventmesh/eventmesh_test.go +++ b/pkg/subscriptionmanager/eventmesh/eventmesh_test.go @@ -28,10 +28,6 @@ import ( controllertesting "github.com/kyma-project/eventing-manager/testing" ) -const ( - domain = "domain.com" -) - type bebSubMgrMock struct { Client dynamic.Interface eventMeshBackend backendeventmesh.Backend @@ -78,8 +74,6 @@ func Test_cleanupEventMesh(t *testing.T) { ClientSecret: "client-secret", TokenEndpoint: bebMock.TokenURL, WebhookActivationTimeout: 0, - WebhookTokenEndpoint: "webhook-token-endpoint", - Domain: domain, EventTypePrefix: controllertesting.EventTypePrefix, BEBNamespace: "/default/ns", Qos: string(types.QosAtLeastOnce), diff --git a/pkg/subscriptionmanager/factory.go b/pkg/subscriptionmanager/factory.go index f50d506e..d0a76fa1 100644 --- a/pkg/subscriptionmanager/factory.go +++ b/pkg/subscriptionmanager/factory.go @@ -20,7 +20,7 @@ var _ ManagerFactory = &Factory{} //go:generate mockery --name=ManagerFactory --outpkg=mocks --case=underscore type ManagerFactory interface { NewJetStreamManager(v1alpha1.Eventing, env.NATSConfig) manager.Manager - NewEventMeshManager() (manager.Manager, error) + NewEventMeshManager(domain string) (manager.Manager, error) } type Factory struct { @@ -51,6 +51,8 @@ func (f Factory) NewJetStreamManager(eventing v1alpha1.Eventing, natsConfig env. f.metricsAddress, f.metricsCollector, f.logger) } -func (f Factory) NewEventMeshManager() (manager.Manager, error) { - return eventmesh.NewSubscriptionManager(f.k8sRestCfg, f.metricsAddress, f.resyncPeriod, f.logger, f.metricsCollector), nil +func (f Factory) NewEventMeshManager(domain string) (manager.Manager, error) { + return eventmesh.NewSubscriptionManager( + f.k8sRestCfg, f.metricsAddress, f.resyncPeriod, f.logger, f.metricsCollector, domain, + ), nil } diff --git a/pkg/subscriptionmanager/mocks/manager_factory.go b/pkg/subscriptionmanager/mocks/manager_factory.go index ed2b5e0c..d672c9ff 100644 --- a/pkg/subscriptionmanager/mocks/manager_factory.go +++ b/pkg/subscriptionmanager/mocks/manager_factory.go @@ -23,25 +23,25 @@ func (_m *ManagerFactory) EXPECT() *ManagerFactory_Expecter { return &ManagerFactory_Expecter{mock: &_m.Mock} } -// NewEventMeshManager provides a mock function with given fields: -func (_m *ManagerFactory) NewEventMeshManager() (manager.Manager, error) { - ret := _m.Called() +// NewEventMeshManager provides a mock function with given fields: domain +func (_m *ManagerFactory) NewEventMeshManager(domain string) (manager.Manager, error) { + ret := _m.Called(domain) var r0 manager.Manager var r1 error - if rf, ok := ret.Get(0).(func() (manager.Manager, error)); ok { - return rf() + if rf, ok := ret.Get(0).(func(string) (manager.Manager, error)); ok { + return rf(domain) } - if rf, ok := ret.Get(0).(func() manager.Manager); ok { - r0 = rf() + if rf, ok := ret.Get(0).(func(string) manager.Manager); ok { + r0 = rf(domain) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(manager.Manager) } } - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(domain) } else { r1 = ret.Error(1) } @@ -55,13 +55,14 @@ type ManagerFactory_NewEventMeshManager_Call struct { } // NewEventMeshManager is a helper method to define mock.On call -func (_e *ManagerFactory_Expecter) NewEventMeshManager() *ManagerFactory_NewEventMeshManager_Call { - return &ManagerFactory_NewEventMeshManager_Call{Call: _e.mock.On("NewEventMeshManager")} +// - domain string +func (_e *ManagerFactory_Expecter) NewEventMeshManager(domain interface{}) *ManagerFactory_NewEventMeshManager_Call { + return &ManagerFactory_NewEventMeshManager_Call{Call: _e.mock.On("NewEventMeshManager", domain)} } -func (_c *ManagerFactory_NewEventMeshManager_Call) Run(run func()) *ManagerFactory_NewEventMeshManager_Call { +func (_c *ManagerFactory_NewEventMeshManager_Call) Run(run func(domain string)) *ManagerFactory_NewEventMeshManager_Call { _c.Call.Run(func(args mock.Arguments) { - run() + run(args[0].(string)) }) return _c } @@ -71,7 +72,7 @@ func (_c *ManagerFactory_NewEventMeshManager_Call) Return(_a0 manager.Manager, _ return _c } -func (_c *ManagerFactory_NewEventMeshManager_Call) RunAndReturn(run func() (manager.Manager, error)) *ManagerFactory_NewEventMeshManager_Call { +func (_c *ManagerFactory_NewEventMeshManager_Call) RunAndReturn(run func(string) (manager.Manager, error)) *ManagerFactory_NewEventMeshManager_Call { _c.Call.Return(run) return _c } diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index b1b34e5a..b2b75dd3 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -119,3 +119,9 @@ func GetCloudEvent(eventType string) event.Event { newEvent.SetID(GetRandString(randStringlength)) return newEvent } + +// IsEmpty returns true if the given string is empty. +// If the given string consists of whitespaces only, it is treated as an empty string. +func IsEmpty(s string) bool { + return len(strings.TrimSpace(s)) == 0 +} diff --git a/pkg/utils/utils_unit_test.go b/pkg/utils/utils_unit_test.go index f23469da..7a338c2f 100644 --- a/pkg/utils/utils_unit_test.go +++ b/pkg/utils/utils_unit_test.go @@ -5,6 +5,7 @@ import ( "reflect" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -211,3 +212,9 @@ func TestGetSinkData(t *testing.T) { }) } } + +func Test_IsEmpty(t *testing.T) { + assert.True(t, IsEmpty("")) + assert.True(t, IsEmpty(" ")) + assert.False(t, IsEmpty("value")) +} diff --git a/test/utils/integration/integration.go b/test/utils/integration/integration.go index 08532fbf..2fc92e56 100644 --- a/test/utils/integration/integration.go +++ b/test/utils/integration/integration.go @@ -6,7 +6,6 @@ import ( "crypto/rand" "fmt" "log" - "os" "path/filepath" "strings" "testing" @@ -155,9 +154,6 @@ func NewTestEnvironment(projectRootDir string, celValidationEnabled bool, } recorder := ctrlMgr.GetEventRecorderFor("eventing-manager-test") - os.Setenv("WEBHOOK_TOKEN_ENDPOINT", "https://oauth2.ev-manager.kymatunas.shoot.canary.k8s-hana.ondemand.com/oauth2/token") - os.Setenv("DOMAIN", "my.test.domain") - // create k8s clients. apiClientSet, err := apiclientset.NewForConfig(ctrlMgr.GetConfig()) if err != nil { @@ -186,7 +182,7 @@ func NewTestEnvironment(projectRootDir string, celValidationEnabled bool, // define subscription manager factory mock. subManagerFactoryMock := new(subscriptionmanagermocks.ManagerFactory) subManagerFactoryMock.On("NewJetStreamManager", mock.Anything, mock.Anything).Return(jetStreamSubManagerMock) - subManagerFactoryMock.On("NewEventMeshManager").Return(eventMeshSubManagerMock, nil) + subManagerFactoryMock.On("NewEventMeshManager", mock.Anything).Return(eventMeshSubManagerMock, nil) // create a new watcher eventingReconciler := eventingctrl.NewReconciler( diff --git a/test/utils/options.go b/test/utils/options.go index 1cb4367d..1688e58e 100644 --- a/test/utils/options.go +++ b/test/utils/options.go @@ -9,8 +9,8 @@ import ( ) func WithEventingCRMinimal() EventingOption { - return func(nats *v1alpha1.Eventing) error { - nats.Spec = v1alpha1.EventingSpec{ + return func(e *v1alpha1.Eventing) error { + e.Spec = v1alpha1.EventingSpec{ Backend: v1alpha1.Backend{ Type: v1alpha1.NatsBackendType, }, @@ -20,29 +20,29 @@ func WithEventingCRMinimal() EventingOption { } func WithEventingCRName(name string) EventingOption { - return func(nats *v1alpha1.Eventing) error { - nats.Name = name + return func(e *v1alpha1.Eventing) error { + e.Name = name return nil } } func WithEventingCRNamespace(namespace string) EventingOption { - return func(nats *v1alpha1.Eventing) error { - nats.Namespace = namespace + return func(e *v1alpha1.Eventing) error { + e.Namespace = namespace return nil } } func WithEventingCRFinalizer(finalizer string) EventingOption { - return func(eventing *v1alpha1.Eventing) error { - controllerutil.AddFinalizer(eventing, finalizer) + return func(e *v1alpha1.Eventing) error { + controllerutil.AddFinalizer(e, finalizer) return nil } } func WithEventingStreamData(natsStorageType string, maxStreamSize string, natsStreamReplicas, maxMsgsPerTopic int) EventingOption { - return func(nats *v1alpha1.Eventing) error { - nats.Spec.Backend.Config = v1alpha1.BackendConfig{ + return func(e *v1alpha1.Eventing) error { + e.Spec.Backend.Config = v1alpha1.BackendConfig{ NATSStreamStorageType: natsStorageType, NATSStreamMaxSize: resource.MustParse(maxStreamSize), NATSStreamReplicas: natsStreamReplicas, @@ -53,8 +53,8 @@ func WithEventingStreamData(natsStorageType string, maxStreamSize string, natsSt } func WithEventingPublisherData(minReplicas, maxReplicas int, requestCPU, requestMemory, limitCPU, limitMemory string) EventingOption { - return func(eventing *v1alpha1.Eventing) error { - eventing.Spec.Publisher = v1alpha1.Publisher{ + return func(e *v1alpha1.Eventing) error { + e.Spec.Publisher = v1alpha1.Publisher{ Replicas: v1alpha1.Replicas{ Min: minReplicas, Max: maxReplicas, @@ -75,8 +75,8 @@ func WithEventingPublisherData(minReplicas, maxReplicas int, requestCPU, request } func WithEventingInvalidBackend() EventingOption { - return func(nats *v1alpha1.Eventing) error { - nats.Spec = v1alpha1.EventingSpec{ + return func(e *v1alpha1.Eventing) error { + e.Spec = v1alpha1.EventingSpec{ Backend: v1alpha1.Backend{ Type: "invalid", }, @@ -86,25 +86,32 @@ func WithEventingInvalidBackend() EventingOption { } func WithEventingEventTypePrefix(eventTypePrefix string) EventingOption { - return func(nats *v1alpha1.Eventing) error { - nats.Spec.Backend.Config.EventTypePrefix = eventTypePrefix + return func(e *v1alpha1.Eventing) error { + e.Spec.Backend.Config.EventTypePrefix = eventTypePrefix + return nil + } +} + +func WithEventingDomain(domain string) EventingOption { + return func(e *v1alpha1.Eventing) error { + e.Spec.Backend.Config.Domain = domain return nil } } func WithEventingLogLevel(logLevel string) EventingOption { - return func(nats *v1alpha1.Eventing) error { - nats.Spec.LogLevel = logLevel + return func(e *v1alpha1.Eventing) error { + e.Spec.LogLevel = logLevel return nil } } func WithEventMeshBackend(eventMeshSecretName string) EventingOption { - return func(eventing *v1alpha1.Eventing) error { - eventing.Spec.Backend = v1alpha1.Backend{ + return func(e *v1alpha1.Eventing) error { + e.Spec.Backend = v1alpha1.Backend{ Type: v1alpha1.EventMeshBackendType, Config: v1alpha1.BackendConfig{ - EventMeshSecret: eventing.Namespace + "/" + eventMeshSecretName, + EventMeshSecret: e.Namespace + "/" + eventMeshSecretName, }, } return nil @@ -112,29 +119,29 @@ func WithEventMeshBackend(eventMeshSecretName string) EventingOption { } func WithNATSBackend() EventingOption { - return func(eventing *v1alpha1.Eventing) error { - eventing.Spec.Backend.Type = v1alpha1.NatsBackendType + return func(e *v1alpha1.Eventing) error { + e.Spec.Backend.Type = v1alpha1.NatsBackendType return nil } } func WithStatusActiveBackend(activeBackend v1alpha1.BackendType) EventingOption { - return func(eventing *v1alpha1.Eventing) error { - eventing.Status.ActiveBackend = activeBackend + return func(e *v1alpha1.Eventing) error { + e.Status.ActiveBackend = activeBackend return nil } } func WithStatusState(state string) EventingOption { - return func(eventing *v1alpha1.Eventing) error { - eventing.Status.State = state + return func(e *v1alpha1.Eventing) error { + e.Status.State = state return nil } } func WithStatusConditions(conditions []metav1.Condition) EventingOption { - return func(eventing *v1alpha1.Eventing) error { - eventing.Status.Conditions = conditions + return func(e *v1alpha1.Eventing) error { + e.Status.Conditions = conditions return nil } } diff --git a/test/utils/utils.go b/test/utils/utils.go index c110ec60..ec5e8c7c 100644 --- a/test/utils/utils.go +++ b/test/utils/utils.go @@ -24,6 +24,7 @@ const ( charset = "abcdefghijklmnopqrstuvwxyz0123456789" randomNameLen = 5 + Domain = "domain.com" NameFormat = "name-%s" NamespaceFormat = "namespace-%s" PublisherProxySuffix = "publisher-proxy" @@ -325,6 +326,16 @@ func NewSubscription(name, namespace string) *eventinv1alpha2.Subscription { } } +func NewConfigMap(name, namespace string) *v1.ConfigMap { + cm := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + } + return cm +} + func FindObjectByKind(kind string, objects []client.Object) (client.Object, error) { for _, obj := range objects { if obj.GetObjectKind().GroupVersionKind().Kind == kind {