Skip to main content
Version: v1.4.0-m.1 (pre-release)

ObservabilityAlertsNotificationChannel

An ObservabilityAlertsNotificationChannel defines a destination for alert notifications. These resources are environment-bound, meaning each channel is associated with a specific OpenChoreo environment.

Default Notification Channel

In each environment, one ObservabilityAlertsNotificationChannel can be marked as the default. The first notification channel created in an environment is automatically marked as the default channel. If an ObservabilityAlertRule is created without explicitly specifying actions.notifications.channels, it will automatically use the default channel for that environment.

Currently, email and webhook notifications are supported. Email bodies can be sent as plain text (text/plain) or HTML (text/html).

Secret Values​

SMTP credentials and secret-backed webhook headers are referenced with a secretKeyRef (name and key). The name refers to a SecretReference in the channel's namespace and the key to one of its spec.data[].secretKey entries, the same way a Workload references secrets. The controller applies an ExternalSecret to the environment's observability plane, which syncs the values from that plane's external secret store into the Secret the observer reads when it sends a notification. While a channel's secret references can't be used, for example because a SecretReference is missing or points at a remote key the channel may not read, OpenChoreo removes the channel's ExternalSecret, so the values stop reaching the observability plane. The control plane never reads the values, and a changed value reaches the observability plane within the SecretReference's refreshInterval, without touching the channel.

For this to work:

  • The environment's ObservabilityPlane or ClusterObservabilityPlane must have a secretStoreRef.
  • The SecretReference must have no targetPlane, or target the channel's observability plane. The easiest way to create one is occ secret create --target-plane ClusterObservabilityPlane/<name> (or ObservabilityPlane/<name> for a namespaced plane), or the Secrets page in Backstage. See Secret Management.
  • The SecretReference's remote keys must be the key the Secret Management API writes for it: <remoteKeyPrefix>/<namespace>/<type>/<name> by default, from the channel's namespace and the SecretReference's spec.template.type (generic, basic-auth, ssh-auth, registry or tls) and name. Webhook channels send header values to a URL of the user's choosing, so a channel can't read any other key, such as secrets of other namespaces or of the platform. SecretReferences created through occ secret create or Backstage always use this key. A SecretReference you write by hand must have the same name and type as the secret it points at.
    • With the - key separator (features.secretManagement.remoteKeySeparator), which can also occur in namespace names, the key must also not be laid out as a key of any other namespace name, whether or not that namespace exists. For example, a channel in namespace team can't read secret-team-generic-registry-creds (its secret registry-creds), which a namespace team-generic could also own as its registry secret creds. So with -, a channel can't use secrets whose names have a type segment followed by - (such as registry-creds), and channels in a namespace with a type segment in its name (such as team-generic) can't use SecretReference-backed secrets. A separator that can't occur in namespace names, such as the default /, avoids this.
Deprecated: referencing Kubernetes Secrets

If no SecretReference has the referenced name, a Kubernetes Secret with that name and key in the channel's namespace on the control plane is used instead, and its values are copied to the observability plane. This is deprecated and will be removed in a future release. Such channels report the DeprecatedKubernetesSecret reason in their SecretsReady condition and get a warning event. All secret references of a channel must resolve the same way: either all to SecretReferences or all to Kubernetes Secrets.

API Version​

openchoreo.dev/v1alpha1

Resource Definition​

Metadata​

ObservabilityAlertsNotificationChannel resources are namespace-scoped.

apiVersion: openchoreo.dev/v1alpha1
kind: ObservabilityAlertsNotificationChannel
metadata:
name: <channel-name>
namespace: <org-namespace>

Spec Fields​

FieldTypeRequiredDescription
environmentstringYesName of the OpenChoreo environment this channel belongs to (Immutable)
isEnvDefaultbooleanNoIf true, this is the default channel for the environment. Default channels are used by alert rules that don't specify a channel. Defaults to false. First channel created in an environment will be marked as the default
typeNotificationChannelTypeYesThe type of notification channel (email or webhook)
emailConfigEmailConfigRequired if type is emailEmail configuration
webhookConfigWebhookConfigRequired if type is webhookWebhook configuration

NotificationChannelType​

ValueDescription
emailEmail notification channel
webhookHTTP webhook notification channel

EmailConfig​

FieldTypeRequiredDescription
fromstringYesThe sender email address
tostring[]YesList of recipient email addresses (minimum 1)
smtpSMTPConfigYesSMTP server configuration
templateEmailTemplateYesEmail subject and body templates using CEL expressions, plus the body content type

WebhookConfig​

FieldTypeRequiredDescription
urlstringYesThe webhook endpoint URL where alerts will be sent (must be a valid URI)
headersmap[string]WebhookHeaderValueNoOptional HTTP headers to include in the webhook request. Each header value can be provided inline or via a secret reference.
payloadTemplatestringNoOptional JSON payload template using CEL expressions. If not provided, the raw alertDetails object will be sent as JSON. CEL expressions use ${...} syntax and have access to alert fields: ${alertName}, ${alertDescription}, ${alertSeverity}, ${alertValue}, etc. Example for Slack: {"text": "Alert: ${alertName}", "blocks": [...]}

WebhookHeaderValue​

Defines a header value that can be provided inline or via a secret reference.

Mutually Exclusive Fields

Exactly one of value or valueFrom must be set (not both, not neither).

FieldTypeRequiredDescription
valuestringNoInline header value (mutually exclusive with valueFrom)
valueFromSecretValueFromNoReference to a secret containing the header value (mutually exclusive with value)

SMTPConfig​

FieldTypeRequiredDescription
hoststringYesSMTP server hostname
portintegerYesSMTP server port
authSMTPAuthYesSMTP authentication credentials
tlsSMTPTLSConfigYesTLS configuration for SMTP

SMTPAuth​

FieldTypeRequiredDescription
usernameSecretValueFromYesUsername for SMTP authentication, from a secret reference
passwordSecretValueFromYesPassword for SMTP authentication, from a secret reference

SMTPTLSConfig​

FieldTypeRequiredDescription
insecureSkipVerifybooleanNoIf true, skips TLS certificate verification (not recommended for production)

EmailTemplate​

Defines the email template using CEL expressions.

FieldTypeRequiredDescription
subjectstringYesCEL expression for the email subject (e.g., "[${alert.severity}] - ${alert.name} Triggered")
bodystringYesCEL expression for the email body
contentTypestringNoMIME type of the rendered email body. Valid values are text/plain and text/html. Defaults to text/plain.

Set contentType to text/html to send an HTML email. The body can contain HTML markup and inline CSS; it is rendered from its CEL expression before the email is sent and is not HTML-escaped.

SecretValueFrom​

Defines how to obtain a secret value.

FieldTypeRequiredDescription
secretKeyRefSecretKeyRefNoReference to a secret value. See Secret Values

SecretKeyRef​

FieldTypeRequiredDescription
namestringYesName of a SecretReference in the channel's namespace (deprecated: or of a Kubernetes Secret, if no SecretReference has this name)
keystringYesA spec.data[].secretKey of the SecretReference (deprecated: or a key of the Kubernetes Secret)

Status​

FieldTypeDescription
observedGenerationintegerGeneration observed by the controller
conditions[]ConditionReady and SecretsReady conditions

Ready reports whether the channel's configuration and secret values are applied to the observability plane. SecretsReady reports whether its secret values are available there. When either is not True, the reason and message explain why:

ReasonConditionMeaning
ReconciledReadyThe channel is applied to the observability plane
ExternalSecretSyncedSecretsReadyThe values are synced from the secret store. The channel is checked again every few minutes, so a later sync failure also shows
NoSecretsReferencedSecretsReadyThe channel references no secret values
DeprecatedKubernetesSecretSecretsReadyThe values are copied from Kubernetes Secrets, which is deprecated
ExternalSecretPendingSecretsReady, ReadyWaiting for the ExternalSecret to sync the channel's current secret references
ExternalSecretNotSyncedSecretsReady, ReadyThe ExternalSecret failed to sync; the message includes its error
ReplacingSecretSecretsReady, ReadyThe channel switched from SecretReferences to Kubernetes Secrets and the previously synced Secret is being removed
SecretNotFoundSecretsReady, ReadyNeither a SecretReference nor a Kubernetes Secret has the referenced name
SecretKeyNotFoundSecretsReady, ReadyThe SecretReference or Secret has no entry for the referenced key
MixedSecretSourcesSecretsReady, ReadySome references resolve to SecretReferences and others to Kubernetes Secrets
SecretStoreNotConfiguredSecretsReady, ReadyThe environment's observability plane has no secretStoreRef. The channel is checked again every few minutes, so it recovers once the plane is fixed
SecretStoreUnreachableSecretsReady, ReadyA SecretReference targets a plane other than the channel's observability plane. The channel is checked again every few minutes
ExternalSecretConflictSecretsReady, ReadyAn ExternalSecret with the channel's name that OpenChoreo didn't create already exists in the channel's namespace on the observability plane. OpenChoreo leaves it alone. The channel is checked again every few minutes
RemoteKeyNotAllowedSecretsReady, ReadyA SecretReference points at a remote key other than the one the Secret Management API writes for it
ObservabilityPlaneUnavailableReadyThe environment's observability plane could not be resolved or reached
ApplyFailedReadyApplying the channel's resources to the observability plane failed

Examples​

Email Notification Channel​

Create the SMTP credentials as a SecretReference in the observability plane's secret store:

occ secret create generic smtp-credentials \
--namespace my-org \
--target-plane ClusterObservabilityPlane/default \
--from-literal=username=<smtp-username> \
--from-literal=password=<smtp-password>

Then reference it from the channel:

apiVersion: openchoreo.dev/v1alpha1
kind: ObservabilityAlertsNotificationChannel
metadata:
name: prod-email-notifications
namespace: my-org
spec:
environment: production
isEnvDefault: true
type: email
emailConfig:
from: "alerts@example.com"
to:
- "admin@example.com"
- "devops@example.com"
smtp:
host: "smtp.example.com"
port: 587
auth:
username:
secretKeyRef:
name: smtp-credentials
key: username
password:
secretKeyRef:
name: smtp-credentials
key: password
tls:
insecureSkipVerify: false
template:
subject: "[OpenChoreo] ${alert.severity}: ${alert.name}"
body: "Alert ${alert.name} triggered at ${alert.startsAt}.\n\nDescription: ${alert.description}"

HTML Email Notification Channel​

Set template.contentType to text/html and use HTML markup in the body to send an HTML email. Inline CSS is supported.

apiVersion: openchoreo.dev/v1alpha1
kind: ObservabilityAlertsNotificationChannel
metadata:
name: prod-html-email-notifications
namespace: my-org
spec:
environment: production
isEnvDefault: false
type: email
emailConfig:
from: "alerts@example.com"
to:
- "admin@example.com"
smtp:
host: "smtp.example.com"
port: 587
auth:
username:
secretKeyRef:
name: smtp-credentials
key: username
password:
secretKeyRef:
name: smtp-credentials
key: password
tls:
insecureSkipVerify: false
template:
contentType: text/html
subject: "[OpenChoreo] ${alertSeverity}: ${alertName}"
body: |
<html>
<body>
<h2 style="color: #d9534f;">Alert: ${alertName}</h2>
<p><strong>Severity:</strong> ${alertSeverity}</p>
<p><strong>Description:</strong> ${alertDescription}</p>
<p><strong>Component:</strong> ${component}</p>
<p><strong>Environment:</strong> ${environment}</p>
</body>
</html>

Webhook Notification Channel​

Create the token as a SecretReference in the observability plane's secret store:

occ secret create generic webhook-token \
--namespace my-org \
--target-plane ClusterObservabilityPlane/default \
--from-literal=token=<webhook-token>

Then reference it from the channel:

apiVersion: openchoreo.dev/v1alpha1
kind: ObservabilityAlertsNotificationChannel
metadata:
name: prod-webhook-notifications
namespace: my-org
spec:
environment: production
isEnvDefault: false
type: webhook
webhookConfig:
url: https://alerts.example.com/webhook
headers:
X-OpenChoreo-Source:
value: observer
Authorization:
valueFrom:
secretKeyRef:
name: webhook-token
key: token
payloadTemplate: |
{
"text": "Alert: ${alertName}",
"severity": "${alertSeverity}",
"description": "${alertDescription}",
"value": "${alertValue}"
}