Skip to content

Commit 7b2aa2c

Browse files
amoralejopenshift-merge-bot[bot]
authored andcommitted
Add support for consuming notifications
This patch is adding a new parameter `notificationsBusInstance` to the Watcher CRD. The value should be the name of a rabbitmq instance which is created specifically for notifications, not RPCs. When it has a value, the watcher-operator will create a TransportURL object for credentials and will configure it as transport_url in the `oslo_messaging_notifications` section so that the dedision engine and potentially any watcher service can consume Nova or other services notifications. Note that, althoug watcher can also send notifications, this patch is disabling sending notifications by default as there is no known consumers of those messages. The patch is also adding functional and kuttl tests. Jira: OSPRH-18694 Signed-off-by: Alfredo Moralejo <amoralej@redhat.com>
1 parent bc4a848 commit 7b2aa2c

22 files changed

Lines changed: 1222 additions & 9 deletions

api/bases/watcher.openstack.org_watchers.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,15 @@ spec:
603603
NodeSelector to target subset of worker nodes running this component. Setting here overrides
604604
any global NodeSelector settings within the Watcher CR.
605605
type: object
606+
notificationsBusInstance:
607+
description: |-
608+
NotificationsBusInstance is the name of the RabbitMqCluster CR to select
609+
the Message Bus Service instance used by the Watcher service to publish and consume notifications
610+
from other services.
611+
If undefined, the value will be inherited from OpenStackControlPlane.
612+
An empty value "" leaves the notification drivers unconfigured and emitting no notifications at all.
613+
Avoid colocating it with RabbitMqClusterName or other message bus instances used for RPC.
614+
type: string
606615
passwordSelectors:
607616
default:
608617
service: WatcherPassword

api/v1beta1/common_types.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,15 @@ type WatcherSpecCore struct {
128128
// +kubebuilder:validation:Minimum=10
129129
// APITimeout for Route and Apache
130130
APITimeout *int `json:"apiTimeout"`
131+
132+
// +kubebuilder:validation:Optional
133+
// NotificationsBusInstance is the name of the RabbitMqCluster CR to select
134+
// the Message Bus Service instance used by the Watcher service to publish and consume notifications
135+
// from other services.
136+
// If undefined, the value will be inherited from OpenStackControlPlane.
137+
// An empty value "" leaves the notification drivers unconfigured and emitting no notifications at all.
138+
// Avoid colocating it with RabbitMqClusterName or other message bus instances used for RPC.
139+
NotificationsBusInstance *string `json:"notificationsBusInstance,omitempty"`
131140
}
132141

133142
// PasswordSelector to identify the DB and AdminUser password from the Secret

api/v1beta1/conditions.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
55
const (
66
// WatcherRabbitMQTransportURLReadyCondition -
77
WatcherRabbitMQTransportURLReadyCondition condition.Type = "WatcherRabbitMQTransportURLReady"
8+
// WatcherNotificationsBusTransportURLReadyCondition -
9+
WatcherNotificationTransportURLReadyCondition condition.Type = "WatcherNotificationTransportURLReady"
810
// WatcherAPIReadyCondition -
911
WatcherAPIReadyCondition condition.Type = "WatcherAPIReady"
1012
// WatcherApplierReadyCondition -
@@ -20,6 +22,12 @@ const (
2022
WatcherRabbitMQTransportURLReadyMessage = "WatcherRabbitMQTransportURL successfully created"
2123
// WatcherRabbitMQTransportURLReadyErrorMessage -
2224
WatcherRabbitMQTransportURLReadyErrorMessage = "WatcherRabbitMQTransportURL error occured %s"
25+
// WatcherNotificationTransportURLReadyRunningMessage -
26+
WatcherNotificationTransportURLReadyRunningMessage = "WatcherNotificationTransportURL creation in progress"
27+
// WatcherNotificationTransportURLReadyMessage -
28+
WatcherNotificationTransportURLReadyMessage = "WatcherNotificationTransportURL successfully created"
29+
// WatcherNotificationTransportURLReadyErrorMessage -
30+
WatcherNotificationTransportURLReadyErrorMessage = "WatcherNotificationTransportURL error occured %s"
2331
// WatcherAPIReadyInitMessage -
2432
WatcherAPIReadyInitMessage = "WatcherAPI creation not started"
2533
// WatcherAPIReadyRunningMessage -

api/v1beta1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/watcher.openstack.org_watchers.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,15 @@ spec:
603603
NodeSelector to target subset of worker nodes running this component. Setting here overrides
604604
any global NodeSelector settings within the Watcher CR.
605605
type: object
606+
notificationsBusInstance:
607+
description: |-
608+
NotificationsBusInstance is the name of the RabbitMqCluster CR to select
609+
the Message Bus Service instance used by the Watcher service to publish and consume notifications
610+
from other services.
611+
If undefined, the value will be inherited from OpenStackControlPlane.
612+
An empty value "" leaves the notification drivers unconfigured and emitting no notifications at all.
613+
Avoid colocating it with RabbitMqClusterName or other message bus instances used for RPC.
614+
type: string
606615
passwordSelectors:
607616
default:
608617
service: WatcherPassword

controllers/watcher_common.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ var (
6565
const (
6666
// TransportURLSelector is the name of key in the secret created by TransportURL
6767
TransportURLSelector = "transport_url"
68+
// NotificationURLSelector is the name of key in the secret created by the notification TransportURL
69+
NotificationURLSelector = "notification_url"
6870
// DatabaseAccount is the name of key in the secret for the name of the Database Acount object
6971
DatabaseAccount = "database_account"
7072
// DatabaseUsername is the name of key in the secret for the user name used to login to the database

controllers/watcher_controller.go

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -201,11 +201,11 @@ func (r *WatcherReconciler) Reconcile(ctx context.Context, req ctrl.Request) (re
201201
// create service DB - end
202202

203203
//
204-
// create RabbitMQ transportURL CR and get the actual URL from the associated secret that is created
204+
// create RPC RabbitMQ transportURL CR and get the actual URL from the associated secret that is created
205205
// not-ready condition is managed here instead of in ensureMQ to distinguish between Error (when receiving)
206206
// an error, or Running when transportURL is empty.
207207
//
208-
transportURL, op, err := r.ensureMQ(ctx, instance, helper, serviceLabels)
208+
transportURL, op, err := r.ensureMQ(ctx, instance, helper, instance.Name+"-watcher-transport", *instance.Spec.RabbitMqClusterName, serviceLabels)
209209
if err != nil {
210210
instance.Status.Conditions.Set(condition.FalseCondition(
211211
watcherv1beta1.WatcherRabbitMQTransportURLReadyCondition,
@@ -226,9 +226,64 @@ func (r *WatcherReconciler) Reconcile(ctx context.Context, req ctrl.Request) (re
226226
return ctrl.Result{RequeueAfter: time.Duration(10) * time.Second}, nil
227227
}
228228

229+
instance.Status.Conditions.MarkTrue(watcherv1beta1.WatcherRabbitMQTransportURLReadyCondition, watcherv1beta1.WatcherRabbitMQTransportURLReadyMessage)
230+
229231
_ = op
230232
// end of TransportURL creation
231233

234+
// create Notification RabbitMQ transportURL CR and get the actual URL from the associated secret that is created
235+
notificationURLSecret := &corev1.Secret{}
236+
237+
if instance.Spec.NotificationsBusInstance != nil && *instance.Spec.NotificationsBusInstance != "" {
238+
instance.Status.Conditions.Set(condition.FalseCondition(
239+
watcherv1beta1.WatcherNotificationTransportURLReadyCondition,
240+
condition.RequestedReason,
241+
condition.SeverityInfo,
242+
watcherv1beta1.WatcherNotificationTransportURLReadyRunningMessage,
243+
))
244+
notificationURL, op, err := r.ensureMQ(ctx, instance, helper, instance.Name+"-watcher-notification", *instance.Spec.NotificationsBusInstance, serviceLabels)
245+
if err != nil {
246+
instance.Status.Conditions.Set(condition.FalseCondition(
247+
watcherv1beta1.WatcherNotificationTransportURLReadyCondition,
248+
condition.ErrorReason,
249+
condition.SeverityWarning,
250+
watcherv1beta1.WatcherNotificationTransportURLReadyErrorMessage,
251+
err.Error()))
252+
return ctrl.Result{}, err
253+
}
254+
255+
if notificationURL == nil {
256+
Log.Info(fmt.Sprintf("Waiting for TransportURL for %s to be created", instance.Name))
257+
instance.Status.Conditions.Set(condition.FalseCondition(
258+
watcherv1beta1.WatcherNotificationTransportURLReadyCondition,
259+
condition.RequestedReason,
260+
condition.SeverityInfo,
261+
watcherv1beta1.WatcherNotificationTransportURLReadyRunningMessage,
262+
))
263+
return ctrl.Result{RequeueAfter: time.Duration(10) * time.Second}, nil
264+
}
265+
instance.Status.Conditions.MarkTrue(watcherv1beta1.WatcherNotificationTransportURLReadyCondition, watcherv1beta1.WatcherNotificationTransportURLReadyMessage)
266+
267+
// NotificationURL Secret
268+
hashNotificationURL, _, notificationSecret, err := ensureSecret(
269+
ctx,
270+
types.NamespacedName{Namespace: instance.Namespace, Name: notificationURL.Status.SecretName},
271+
[]string{
272+
TransportURLSelector,
273+
},
274+
helper.GetClient(),
275+
&instance.Status.Conditions,
276+
r.RequeueTimeout,
277+
)
278+
if err != nil || hashNotificationURL == "" {
279+
// Empty hash means that there is some problem retrieving the key from the secret
280+
return ctrl.Result{}, errors.New("error retrieving required data from notificationURL secret")
281+
}
282+
notificationURLSecret = &notificationSecret
283+
_ = op
284+
}
285+
286+
// end of Notification TransportURL creation
232287
// Check we have the required inputs
233288
// Top level secret
234289
hash, _, inputSecret, err := ensureSecret(
@@ -300,7 +355,7 @@ func (r *WatcherReconciler) Reconcile(ctx context.Context, req ctrl.Request) (re
300355

301356
// End of Prometheus config secret
302357

303-
subLevelSecretName, err := r.createSubLevelSecret(ctx, helper, instance, transporturlSecret, inputSecret, db)
358+
subLevelSecretName, err := r.createSubLevelSecret(ctx, helper, instance, transporturlSecret, notificationURLSecret, inputSecret, db)
304359
if err != nil {
305360
return ctrl.Result{}, nil
306361
}
@@ -605,21 +660,23 @@ func (r *WatcherReconciler) ensureMQ(
605660
ctx context.Context,
606661
instance *watcherv1beta1.Watcher,
607662
h *helper.Helper,
663+
transportURLName string,
664+
messageBusInstance string,
608665
serviceLabels map[string]string,
609666
) (*rabbitmqv1.TransportURL, controllerutil.OperationResult, error) {
610667
Log := r.GetLogger(ctx)
611-
Log.Info(fmt.Sprintf("Reconciling the RabbitMQ TransportURL for '%s'", instance.Name))
668+
Log.Info(fmt.Sprintf("Reconciling the RabbitMQ TransportURL '%s' for '%s'", transportURLName, instance.Name))
612669

613670
transportURL := &rabbitmqv1.TransportURL{
614671
ObjectMeta: metav1.ObjectMeta{
615-
Name: fmt.Sprintf("%s-watcher-transport", instance.Name),
672+
Name: transportURLName,
616673
Namespace: instance.Namespace,
617674
Labels: serviceLabels,
618675
},
619676
}
620677

621678
op, err := controllerutil.CreateOrUpdate(ctx, r.Client, transportURL, func() error {
622-
transportURL.Spec.RabbitmqClusterName = *instance.Spec.RabbitMqClusterName
679+
transportURL.Spec.RabbitmqClusterName = messageBusInstance
623680

624681
err := controllerutil.SetControllerReference(instance, transportURL, r.Scheme)
625682
return err
@@ -656,7 +713,6 @@ func (r *WatcherReconciler) ensureMQ(
656713
"the TransportURL secret %s does not have 'transport_url' field", transportURL.Status.SecretName)
657714
}
658715

659-
instance.Status.Conditions.MarkTrue(watcherv1beta1.WatcherRabbitMQTransportURLReadyCondition, watcherv1beta1.WatcherRabbitMQTransportURLReadyMessage)
660716
return transportURL, op, nil
661717
}
662718

@@ -810,13 +866,15 @@ func (r *WatcherReconciler) createSubLevelSecret(
810866
helper *helper.Helper,
811867
instance *watcherv1beta1.Watcher,
812868
transportURLSecret corev1.Secret,
869+
notificationURLSecret *corev1.Secret,
813870
inputSecret corev1.Secret,
814871
db *mariadbv1.Database,
815872
) (string, error) {
816873
Log := r.GetLogger(ctx)
817874
Log.Info(fmt.Sprintf("Creating SubCr Level Secret for '%s'", instance.Name))
818875
databaseAccount := db.GetAccount()
819876
databaseSecret := db.GetSecret()
877+
820878
data := map[string]string{
821879
*instance.Spec.PasswordSelectors.Service: string(inputSecret.Data[*instance.Spec.PasswordSelectors.Service]),
822880
TransportURLSelector: string(transportURLSecret.Data[TransportURLSelector]),
@@ -825,6 +883,7 @@ func (r *WatcherReconciler) createSubLevelSecret(
825883
DatabasePassword: string(databaseSecret.Data[mariadbv1.DatabasePasswordSelector]),
826884
DatabaseHostname: db.GetDatabaseHostname(),
827885
watcher.GlobalCustomConfigFileName: instance.Spec.CustomServiceConfig,
886+
NotificationURLSelector: string(notificationURLSecret.Data[TransportURLSelector]),
828887
}
829888
secretName := instance.Name
830889

controllers/watcherapi_controller.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,10 @@ func (r *WatcherAPIReconciler) generateServiceConfigs(
466466
"PrometheusCaCertPath": prometheusCaCertPath,
467467
}
468468

469+
if string(secret.Data[NotificationURLSelector]) != "" {
470+
templateParameters["NotificationURL"] = string(secret.Data[NotificationURLSelector])
471+
}
472+
469473
// create httpd vhost template parameters
470474
httpdVhostConfig := map[string]interface{}{}
471475
for _, endpt := range []service.Endpoint{service.EndpointInternal, service.EndpointPublic} {

controllers/watcherapplier_controller.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,10 @@ func (r *WatcherApplierReconciler) generateServiceConfigs(
429429
"CaFilePath": CaFilePath,
430430
}
431431

432+
if string(secret.Data[NotificationURLSelector]) != "" {
433+
templateParameters["NotificationURL"] = string(secret.Data[NotificationURLSelector])
434+
}
435+
432436
return GenerateConfigsGeneric(ctx, helper, instance, envVars, templateParameters, customData, labels, false)
433437
}
434438

controllers/watcherdecisionengine_controller.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,10 @@ func (r *WatcherDecisionEngineReconciler) generateServiceConfigs(
542542
"PrometheusCaCertPath": prometheusCaCertPath,
543543
}
544544

545+
if string(secret.Data[NotificationURLSelector]) != "" {
546+
templateParameters["NotificationURL"] = string(secret.Data[NotificationURLSelector])
547+
}
548+
545549
return GenerateConfigsGeneric(ctx, helper, instance, envVars, templateParameters, customData, labels, false)
546550
}
547551

0 commit comments

Comments
 (0)