diff --git a/README.md b/README.md index 5b44b1df93..aa591e72b0 100644 --- a/README.md +++ b/README.md @@ -410,6 +410,12 @@ MongoDB + + + MongoDB Atlas
+ MongoDB Atlas +
+ MySQL
diff --git a/docs/mint.json b/docs/mint.json index 7672da965b..b0312f68dd 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -212,6 +212,7 @@ "providers/documentation/mock-provider", "providers/documentation/monday-provider", "providers/documentation/mongodb-provider", + "providers/documentation/mongodbatlas-provider", "providers/documentation/mysql-provider", "providers/documentation/netbox-provider", "providers/documentation/netdata-provider", diff --git a/docs/providers/documentation/mongodbatlas-provider.mdx b/docs/providers/documentation/mongodbatlas-provider.mdx new file mode 100644 index 0000000000..7be3b25acc --- /dev/null +++ b/docs/providers/documentation/mongodbatlas-provider.mdx @@ -0,0 +1,69 @@ +--- +title: "MongoDB Atlas Provider" +sidebarTitle: "MongoDB Atlas" +description: "MongoDB Atlas provider allows pulling alerts from Atlas projects and receiving Atlas alert webhooks." +--- +import AutoGeneratedSnippet from '/snippets/providers/mongodbatlas-snippet-autogenerated.mdx'; + + + +import ProviderLogo from '@components/ProviderLogo'; + + + +# MongoDB Atlas Provider + +The MongoDB Atlas provider connects Keep to an Atlas project. It supports two ways of getting alerts into Keep: + +1. **Pulling** open, tracking, and closed alerts from the [Atlas Administration API](https://www.mongodb.com/docs/api/doc/atlas-admin-api-v2/operation/operation-listalerts). +2. **Receiving** alert notifications pushed by an Atlas [webhook integration](https://www.mongodb.com/docs/atlas/tutorial/webhook-integration/). + +## Setup + +### Prerequisites + +1. An Atlas project. +2. An API key pair (organization or project) with the **Project Read Only** role on the project. See [Grant Programmatic Access to Atlas](https://www.mongodb.com/docs/atlas/configure-api-access/). +3. The project (group) ID: a 24-character hex string, found under **Project Settings**. + +### Configuration + +```yaml +authentication: + api_public_key: "abcdefgh" # Atlas API public key + api_private_key: "00000000-0000-0000-0000-000000000000" # Atlas API private key + group_id: "5d1b6f8e8c2e4e2d3c4a5b6d" # Atlas project (group) ID + # api_url: "https://cloud.mongodbgov.com" # only for Atlas for Government +``` + +If your Atlas project restricts API access by IP, add Keep's IP to the API key's access list. + +### Webhook configuration + +To have Atlas push alerts to Keep: + +1. In Atlas, go to your project and click **Integrations**. +2. Choose **Webhook** and click **Configure**. +3. Set the URL to your Keep webhook URL, with your API key as the `api_key` query parameter. +4. In **Alerts** > **Alert Settings**, select **Webhook** as a notification method on the alert configurations you want in Keep. + +Notes: + +- Atlas webhook requests can't carry custom headers, so authentication uses the `api_key` query parameter rather than the `X-API-KEY` header. +- Atlas payloads carry a status (`OPEN`, `TRACKING`, `CLOSED`) but no severity, so alerts arrive in Keep with `warning` severity. Use Keep mapping rules if you want severities derived from `eventTypeName`. +- Alerts are deduplicated on the Atlas alert `id`, which is stable across an alert's open/close lifecycle. + +## Status mapping + +| Atlas status | Keep status | +| ------------ | ----------- | +| `OPEN` | `firing` | +| `TRACKING` | `pending` | +| `CLOSED`, `CANCELLED` | `resolved` | +| acknowledged (via `acknowledgedUntil`) | `acknowledged` | + +## Useful links + +- [Atlas alert basics](https://www.mongodb.com/docs/atlas/alert-basics/) +- [Configure alerts](https://www.mongodb.com/docs/atlas/configure-alerts/) +- [Webhook integration](https://www.mongodb.com/docs/atlas/tutorial/webhook-integration/) diff --git a/docs/providers/overview.mdx b/docs/providers/overview.mdx index 332c53fe4f..d35e4e561b 100644 --- a/docs/providers/overview.mdx +++ b/docs/providers/overview.mdx @@ -532,6 +532,14 @@ By leveraging Keep Providers, users are able to deeply integrate Keep with the t } > + + } +> + Alert Settings, add or edit alert configurations and select Webhook as a notification method. + +Atlas does not include a severity in webhook payloads, so alerts arrive with warning severity and can be re-mapped in Keep. + diff --git a/keep-ui/public/icons/mongodbatlas-icon.png b/keep-ui/public/icons/mongodbatlas-icon.png new file mode 100644 index 0000000000..e58a4a685b Binary files /dev/null and b/keep-ui/public/icons/mongodbatlas-icon.png differ diff --git a/keep/providers/mongodbatlas_provider/__init__.py b/keep/providers/mongodbatlas_provider/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/keep/providers/mongodbatlas_provider/mongodbatlas_provider.py b/keep/providers/mongodbatlas_provider/mongodbatlas_provider.py new file mode 100644 index 0000000000..141e010fa1 --- /dev/null +++ b/keep/providers/mongodbatlas_provider/mongodbatlas_provider.py @@ -0,0 +1,226 @@ +""" +MongodbatlasProvider is a class that allows to pull alerts from MongoDB Atlas +and to receive Atlas alert webhooks. +""" + +import dataclasses +import datetime + +import pydantic +import requests +from requests.auth import HTTPDigestAuth + +from keep.api.models.alert import AlertDto, AlertSeverity, AlertStatus +from keep.contextmanager.contextmanager import ContextManager +from keep.exceptions.provider_exception import ProviderException +from keep.providers.base.base_provider import BaseProvider +from keep.providers.models.provider_config import ProviderConfig, ProviderScope + + +@pydantic.dataclasses.dataclass +class MongodbatlasProviderAuthConfig: + """MongoDB Atlas authentication configuration.""" + + api_public_key: str = dataclasses.field( + metadata={ + "required": True, + "description": "Atlas API public key", + "hint": "Organization or project API key with Project Read Only access", + "documentation_url": "https://www.mongodb.com/docs/atlas/configure-api-access/", + } + ) + api_private_key: str = dataclasses.field( + metadata={ + "required": True, + "description": "Atlas API private key", + "sensitive": True, + } + ) + group_id: str = dataclasses.field( + metadata={ + "required": True, + "description": "Atlas project (group) ID", + "hint": "24-character hexadecimal string, found under Project Settings", + } + ) + api_url: str = dataclasses.field( + default="https://cloud.mongodb.com", + metadata={ + "required": False, + "description": "Atlas API base URL", + "hint": "Change for MongoDB Atlas for Government (https://cloud.mongodbgov.com)", + }, + ) + + +class MongodbatlasProvider(BaseProvider): + """Pull alerts from MongoDB Atlas and receive Atlas alert webhooks.""" + + PROVIDER_DISPLAY_NAME = "MongoDB Atlas" + PROVIDER_CATEGORY = ["Database", "Monitoring"] + PROVIDER_TAGS = ["alert"] + FINGERPRINT_FIELDS = ["id"] + + ATLAS_API_VERSION_HEADER = "application/vnd.atlas.2023-01-01+json" + PAGE_SIZE = 500 # Atlas API maximum for itemsPerPage + + PROVIDER_SCOPES = [ + ProviderScope( + name="Project Read Only", + description="Read access to the project, required to list alerts", + mandatory=True, + documentation_url="https://www.mongodb.com/docs/atlas/reference/user-roles/#mongodb-authrole-Project-Read-Only", + alias="Project Read Only", + ), + ] + + STATUS_MAP = { + "OPEN": AlertStatus.FIRING, + "TRACKING": AlertStatus.PENDING, + "CLOSED": AlertStatus.RESOLVED, + "CANCELLED": AlertStatus.RESOLVED, + } + + webhook_description = "" + webhook_template = "" + webhook_markdown = """ +To send alerts from MongoDB Atlas to Keep: + +1. In Atlas, go to your project and click Integrations. +2. Choose Webhook and click Configure. +3. Set the URL to {keep_webhook_api_url}?api_key={api_key} +4. Leave the secret empty (Keep authenticates via the api_key query parameter) and save. +5. In Alerts > Alert Settings, add or edit alert configurations and select Webhook as a notification method. + +Atlas does not include a severity in webhook payloads, so alerts arrive with warning severity and can be re-mapped in Keep. +""" + + def __init__( + self, context_manager: ContextManager, provider_id: str, config: ProviderConfig + ): + super().__init__(context_manager, provider_id, config) + + def validate_config(self): + self.authentication_config = MongodbatlasProviderAuthConfig( + **self.config.authentication + ) + + def dispose(self): + pass + + @property + def _auth(self) -> HTTPDigestAuth: + return HTTPDigestAuth( + self.authentication_config.api_public_key, + self.authentication_config.api_private_key, + ) + + def _alerts_url(self) -> str: + base = self.authentication_config.api_url.rstrip("/") + return ( + f"{base}/api/atlas/v2/groups/{self.authentication_config.group_id}/alerts" + ) + + def validate_scopes(self) -> dict[str, bool | str]: + try: + response = requests.get( + self._alerts_url(), + auth=self._auth, + headers={"Accept": self.ATLAS_API_VERSION_HEADER}, + params={"itemsPerPage": 1}, + timeout=10, + ) + response.raise_for_status() + return {"Project Read Only": True} + except requests.exceptions.HTTPError as e: + if e.response.status_code in (401, 403): + return { + "Project Read Only": "Authentication failed, check the API key pair and that it has Project Read Only access" + } + return {"Project Read Only": str(e)} + except requests.exceptions.RequestException as e: + return {"Project Read Only": str(e)} + + def _get_alerts(self) -> list[AlertDto]: + alerts = [] + page_num = 1 + while True: + try: + response = requests.get( + self._alerts_url(), + auth=self._auth, + headers={"Accept": self.ATLAS_API_VERSION_HEADER}, + params={"itemsPerPage": self.PAGE_SIZE, "pageNum": page_num}, + timeout=30, + ) + response.raise_for_status() + except requests.exceptions.RequestException as e: + raise ProviderException( + f"Failed to get alerts from MongoDB Atlas: {e}" + ) from e + data = response.json() + results = data.get("results", []) + alerts.extend(self._format_alert(alert) for alert in results) + if not results or page_num * self.PAGE_SIZE >= data.get("totalCount", 0): + break + page_num += 1 + return alerts + + @staticmethod + def _format_alert( + event: dict, provider_instance: "BaseProvider" = None + ) -> AlertDto: + # Webhook payloads use the same schema as the Admin API alert resource, + # so this formatter serves both the pull and push paths. Atlas does not + # include a severity, only a status. + status = MongodbatlasProvider.STATUS_MAP.get( + event.get("status"), AlertStatus.FIRING + ) + if event.get("acknowledgedUntil") and status == AlertStatus.FIRING: + status = AlertStatus.ACKNOWLEDGED + current_value = event.get("currentValue") or {} + return AlertDto( + id=event.get("id"), + name=event.get("eventTypeName", "MongoDB Atlas alert"), + status=status, + severity=AlertSeverity.WARNING, + lastReceived=event.get("updated") + or event.get("created") + or datetime.datetime.now(tz=datetime.timezone.utc).isoformat(), + description=event.get("humanReadable"), + source=["mongodbatlas"], + cluster=event.get("clusterName"), + replicaSet=event.get("replicaSetName"), + host=event.get("hostnameAndPort"), + metric=event.get("metricName"), + currentValue=current_value.get("number"), + currentValueUnits=current_value.get("units"), + groupId=event.get("groupId"), + alertConfigId=event.get("alertConfigId"), + ) + + +if __name__ == "__main__": + import logging + import os + + logging.basicConfig(level=logging.DEBUG, handlers=[logging.StreamHandler()]) + context_manager = ContextManager( + tenant_id="singletenant", + workflow_id="test", + ) + config = ProviderConfig( + authentication={ + "api_public_key": os.environ.get("ATLAS_API_PUBLIC_KEY"), + "api_private_key": os.environ.get("ATLAS_API_PRIVATE_KEY"), + "group_id": os.environ.get("ATLAS_GROUP_ID"), + } + ) + provider = MongodbatlasProvider( + context_manager=context_manager, + provider_id="mongodbatlas", + config=config, + ) + print(provider.validate_scopes()) + alerts = provider.get_alerts() + print(f"Got {len(alerts)} alerts")