diff --git a/api/operator/v1alpha1/eventing_types.go b/api/operator/v1alpha1/eventing_types.go index c7b21be02..d11a77df7 100644 --- a/api/operator/v1alpha1/eventing_types.go +++ b/api/operator/v1alpha1/eventing_types.go @@ -92,6 +92,7 @@ type EventingStatus struct { ActiveBackend BackendType `json:"activeBackend"` BackendConfigHash int64 `json:"specHash"` State string `json:"state"` + PublisherService string `json:"publisherService,omitempty"` Conditions []kmetav1.Condition `json:"conditions,omitempty"` } diff --git a/api/operator/v1alpha1/status.go b/api/operator/v1alpha1/status.go index f79fda59b..2f0e67c7b 100644 --- a/api/operator/v1alpha1/status.go +++ b/api/operator/v1alpha1/status.go @@ -1,6 +1,7 @@ package v1alpha1 import ( + "fmt" "reflect" natsv1alpha1 "github.com/kyma-project/nats-manager/api/v1alpha1" @@ -134,3 +135,13 @@ func (es *EventingStatus) IsEqual(status EventingStatus) bool { return reflect.DeepEqual(thisWithoutCond, statusWithoutCond) && natsv1alpha1.ConditionsEquals(es.Conditions, status.Conditions) } + +// ClearPublisherService clears the PublisherService. +func (es *EventingStatus) ClearPublisherService() { + es.PublisherService = "" +} + +// SetPublisherService sets the PublisherService from the given service name and namespace. +func (es *EventingStatus) SetPublisherService(name, namespace string) { + es.PublisherService = fmt.Sprintf("%s.%s", name, namespace) +} diff --git a/api/operator/v1alpha1/status_test.go b/api/operator/v1alpha1/status_test.go index 4ac9a8337..af39b57b6 100644 --- a/api/operator/v1alpha1/status_test.go +++ b/api/operator/v1alpha1/status_test.go @@ -29,3 +29,71 @@ func TestClearConditions(t *testing.T) { // then require.Len(t, givenEventingStatus.Conditions, 0) } + +func TestClearPublisherService(t *testing.T) { + // given + t.Parallel() + testCases := []struct { + name string + givenStatus EventingStatus + givenServiceName string + givenServiceNamespace string + wantStatus EventingStatus + }{ + { + name: "should clear the publisher service", + givenStatus: EventingStatus{ + PublisherService: "test-service.test-namespace", + }, + givenServiceName: "test-service", + givenServiceNamespace: "test-namespace", + wantStatus: EventingStatus{ + PublisherService: "", + }, + }, + } + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + // when + tc.givenStatus.ClearPublisherService() + + // then + require.Equal(t, tc.wantStatus, tc.givenStatus) + }) + } +} + +func TestSetPublisherService(t *testing.T) { + // given + t.Parallel() + testCases := []struct { + name string + givenStatus EventingStatus + givenServiceName string + givenServiceNamespace string + wantStatus EventingStatus + }{ + { + name: "should set the correct publisher service", + givenStatus: EventingStatus{ + PublisherService: "", + }, + givenServiceName: "test-service", + givenServiceNamespace: "test-namespace", + wantStatus: EventingStatus{ + PublisherService: "test-service.test-namespace", + }, + }, + } + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + // when + tc.givenStatus.SetPublisherService(tc.givenServiceName, tc.givenServiceNamespace) + + // then + require.Equal(t, tc.wantStatus, tc.givenStatus) + }) + } +} diff --git a/config/crd/bases/operator.kyma-project.io_eventings.yaml b/config/crd/bases/operator.kyma-project.io_eventings.yaml index 67b7fc495..dcf1c4bb6 100644 --- a/config/crd/bases/operator.kyma-project.io_eventings.yaml +++ b/config/crd/bases/operator.kyma-project.io_eventings.yaml @@ -67,7 +67,7 @@ spec: requests: cpu: 40m memory: 256Mi - description: EventingSpec defines the desired state of Eventing + description: EventingSpec defines the desired state of Eventing. properties: annotations: additionalProperties: @@ -265,7 +265,7 @@ spec: - backend type: object status: - description: EventingStatus defines the observed state of Eventing + description: EventingStatus defines the observed state of Eventing. properties: activeBackend: type: string @@ -337,6 +337,8 @@ spec: - type type: object type: array + publisherService: + type: string specHash: format: int64 type: integer diff --git a/docs/user/02-configuration.md b/docs/user/02-configuration.md index 65a72b6d4..51d3b237e 100644 --- a/docs/user/02-configuration.md +++ b/docs/user/02-configuration.md @@ -65,9 +65,10 @@ Use the following sample CRs as guidance. Each can be applied immediately when y | **conditions.​message** (required) | string | message is a human readable message indicating details about the transition. This may be an empty string. | | **conditions.​observedGeneration** | integer | observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. | | **conditions.​reason** (required) | string | reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. | -| **conditions.​status** (required) | string | status of the condition, one of `True`, `False`, `Unknown`. | +| **conditions.​status** (required) | string | status of the condition, one of `True`, `False`, `Unknown`. | | **conditions.​type** (required) | string | type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) | +| **publisherService** | string | | | **specHash** (required) | integer | | | **state** (required) | string | | - \ No newline at end of file + diff --git a/internal/controller/operator/eventing/integrationtests/controller/integration_test.go b/internal/controller/operator/eventing/integrationtests/controller/integration_test.go index 422b9eb5c..3c4fe58e0 100644 --- a/internal/controller/operator/eventing/integrationtests/controller/integration_test.go +++ b/internal/controller/operator/eventing/integrationtests/controller/integration_test.go @@ -7,8 +7,6 @@ import ( "os" "testing" - 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" @@ -25,11 +23,12 @@ import ( "github.com/kyma-project/eventing-manager/test/matchers" "github.com/kyma-project/eventing-manager/test/utils" testutilsintegration "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" ) const ( - projectRootDir = "../../../../../../" - eventTypePrefix = "test-prefix" + projectRootDir = "../../../../../../" ) var testEnvironment *testutilsintegration.TestEnvironment //nolint:gochecknoglobals // used in tests @@ -214,6 +213,24 @@ func Test_CreateEventingCR_NATS(t *testing.T) { // check if webhook configurations are updated with correct CABundle. testEnvironment.EnsureCABundleInjectedIntoWebhooks(t) } + + // check PublisherService in the EventingCR status + eventingCR, err := testEnvironment.GetEventingFromK8s(tc.givenEventing.Name, givenNamespace) + require.NoError(t, err) + require.NotNil(t, eventingCR) + switch eventingCR.Status.State { + case operatorv1alpha1.StateReady: + { + serviceName := eventing.GetPublisherPublishServiceName(*eventingCR) + wantPublisherService := fmt.Sprintf("%s.%s", serviceName, eventingCR.Namespace) + require.Equal(t, wantPublisherService, eventingCR.Status.PublisherService) + } + default: + { + const wantPublisherService = "" + require.Equal(t, wantPublisherService, eventingCR.Status.PublisherService) + } + } }) } } @@ -274,12 +291,12 @@ func Test_UpdateEventingCR(t *testing.T) { }() // get Eventing CR. - eventing, err := testEnvironment.GetEventingFromK8s(tc.givenExistingEventing.Name, givenNamespace) + eventingCR, err := testEnvironment.GetEventingFromK8s(tc.givenExistingEventing.Name, givenNamespace) require.NoError(t, err) // when // update NATS CR. - newEventing := eventing.DeepCopy() + newEventing := eventingCR.DeepCopy() newEventing.Spec = tc.givenNewEventingForUpdate.Spec testEnvironment.EnsureK8sResourceUpdated(t, newEventing) @@ -287,6 +304,21 @@ func Test_UpdateEventingCR(t *testing.T) { testEnvironment.EnsureEventingSpecPublisherReflected(t, newEventing) testEnvironment.EnsureEventingReplicasReflected(t, newEventing) testEnvironment.EnsureDeploymentOwnerReferenceSet(t, tc.givenExistingEventing) + + // check PublisherService in the EventingCR status + switch eventingCR.Status.State { + case operatorv1alpha1.StateReady: + { + serviceName := eventing.GetPublisherPublishServiceName(*eventingCR) + wantPublisherService := fmt.Sprintf("%s.%s", serviceName, eventingCR.Namespace) + require.Equal(t, wantPublisherService, eventingCR.Status.PublisherService) + } + default: + { + const wantPublisherService = "" + require.Equal(t, wantPublisherService, eventingCR.Status.PublisherService) + } + } }) } } @@ -680,6 +712,24 @@ func Test_CreateEventingCR_EventMesh(t *testing.T) { // check if webhook configurations are updated with correct CABundle. testEnvironment.EnsureCABundleInjectedIntoWebhooks(t) } + + // check PublisherService in the EventingCR status + eventingCR, err := testEnvironment.GetEventingFromK8s(tc.givenEventing.Name, givenNamespace) + require.NoError(t, err) + require.NotNil(t, eventingCR) + switch eventingCR.Status.State { + case operatorv1alpha1.StateReady: + { + serviceName := eventing.GetPublisherPublishServiceName(*eventingCR) + wantPublisherService := fmt.Sprintf("%s.%s", serviceName, eventingCR.Namespace) + require.Equal(t, wantPublisherService, eventingCR.Status.PublisherService) + } + default: + { + const wantPublisherService = "" + require.Equal(t, wantPublisherService, eventingCR.Status.PublisherService) + } + } }) } } diff --git a/internal/controller/operator/eventing/status.go b/internal/controller/operator/eventing/status.go index 63562514b..6e3134bb1 100644 --- a/internal/controller/operator/eventing/status.go +++ b/internal/controller/operator/eventing/status.go @@ -12,6 +12,7 @@ import ( kctrl "sigs.k8s.io/controller-runtime" operatorv1alpha1 "github.com/kyma-project/eventing-manager/api/operator/v1alpha1" + "github.com/kyma-project/eventing-manager/pkg/eventing" ) const RequeueTimeForStatusCheck = 10 @@ -153,29 +154,36 @@ func (r *Reconciler) updateStatus(ctx context.Context, oldEventing, newEventing return nil } -func (r *Reconciler) handleEventingState(ctx context.Context, deployment *kappsv1.Deployment, eventing *operatorv1alpha1.Eventing, log *zap.SugaredLogger) (kctrl.Result, error) { +func (r *Reconciler) handleEventingState(ctx context.Context, deployment *kappsv1.Deployment, + eventingCR *operatorv1alpha1.Eventing, log *zap.SugaredLogger) (kctrl.Result, error) { + // Clear the publisher service until the publisher proxy is ready. + eventingCR.Status.ClearPublisherService() + // checking if publisher proxy is ready. // get k8s deployment for publisher proxy deployment, err := r.kubeClient.GetDeployment(ctx, deployment.Name, deployment.Namespace) if err != nil { - eventing.Status.UpdateConditionPublisherProxyReady(kmetav1.ConditionFalse, + eventingCR.Status.UpdateConditionPublisherProxyReady(kmetav1.ConditionFalse, operatorv1alpha1.ConditionReasonDeploymentStatusSyncFailed, err.Error()) - return kctrl.Result{}, r.syncStatusWithPublisherProxyErr(ctx, eventing, err, log) + return kctrl.Result{}, r.syncStatusWithPublisherProxyErr(ctx, eventingCR, err, log) } if !IsDeploymentReady(deployment) { - eventing.Status.SetStateProcessing() - eventing.Status.UpdateConditionPublisherProxyReady(kmetav1.ConditionFalse, + eventingCR.Status.SetStateProcessing() + eventingCR.Status.UpdateConditionPublisherProxyReady(kmetav1.ConditionFalse, operatorv1alpha1.ConditionReasonProcessing, operatorv1alpha1.ConditionPublisherProxyProcessingMessage) log.Info("Reconciliation successful: waiting for publisher proxy to get ready...") - return kctrl.Result{RequeueAfter: RequeueTimeForStatusCheck * time.Second}, r.syncEventingStatus(ctx, eventing, log) + return kctrl.Result{RequeueAfter: RequeueTimeForStatusCheck * time.Second}, r.syncEventingStatus(ctx, eventingCR, log) } - // - eventing.Status.SetPublisherProxyReadyToTrue() + + eventingCR.Status.SetPublisherProxyReadyToTrue() + + // Set the publisher service after the publisher proxy is ready. + eventingCR.Status.SetPublisherService(eventing.GetPublisherPublishServiceName(*eventingCR), eventingCR.Namespace) // @TODO: emit events for any change in conditions log.Info("Reconciliation successful") - return kctrl.Result{}, r.syncEventingStatus(ctx, eventing, log) + return kctrl.Result{}, r.syncEventingStatus(ctx, eventingCR, log) } // to be able to mock this function in tests.