Skip to content

Commit

Permalink
Merge branch 'main' into feature/basic-rbac
Browse files Browse the repository at this point in the history
  • Loading branch information
shahargl authored Jan 8, 2024
2 parents 812c167 + 980d27b commit af748b5
Show file tree
Hide file tree
Showing 12 changed files with 371 additions and 36 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,7 @@ To turn PostHog off, set the `DISABLE_POSTHOG=true` environment variable and rem
#### Spinning up Keep with docker-compose
The easiest way to start with Keep is to run it via docker-compose:
```shell
wget -O docker-compose.yml https://raw.githubusercontent.com/keephq/keep/main/docker-compose.yml
docker-compose -f docker-compose.yml up
curl https://raw.githubusercontent.com/keephq/keep/main/start.sh | sh
```
The UI is now available at http://localhost:3000 and the backend is available at http://localhost:8080.
#### Local development
Expand Down
1 change: 1 addition & 0 deletions docs/mint.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"providers/documentation/cloudwatch-metrics",
"providers/documentation/console-provider",
"providers/documentation/datadog-provider",
"providers/documentation/ilert-provider",
"providers/documentation/kibana-provider",
"providers/documentation/discord-provider",
"providers/documentation/elastic-provider",
Expand Down
48 changes: 48 additions & 0 deletions docs/providers/documentation/ilert-provider.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
title: "ILert"
sidebarTitle: "ILert Provider"
description: "ILert provider allows you to create, update, and resolve incidents in ILert for effective incident management and response."
---

## Inputs

- `summary`: str: A brief summary of the incident or situation you're reporting.
- `status`: IlertIncidentStatus = IlertIncidentStatus.INVESTIGATING: The current status of the incident (e.g., INVESTIGATING, RESOLVED, MONITORING, IDENTIFIED).
- `message`: str = "": A detailed message describing the incident or situation.
- `affectedServices`: str = "[]": A JSON string representing the list of affected services and their statuses.
- `id`: str = "0": The ID of the incident to update. If set to "0", a new incident will be created.

## Outputs

_No information yet, feel free to contribute it using the "Edit this page" link at the bottom of the page_

## Authentication Parameters

The `ilert_token` is required for connecting to the ILert provider. This should be a valid API token provided by ILert.

## Connecting with the Provider

### API Token

To obtain the ILert API token, follow these steps:

1. Log in to your ILert account.
2. Navigate to the "API Tokens" section under your user profile or account settings.
3. Generate a new API token.
4. Make sure "Read Permission" and "Write Permission" are checked.
5. Click on "Save"

Ensure you have the necessary permissions assigned to the token for creating and updating incidents.

## Scopes

ILert integration does not require specific scopes to be set for API token as permissions are managed directly within ILert's platform.

## Notes

_No information yet, feel free to contribute it using the "Edit this page" link at the bottom of the page_

## Useful Links

- [ILert API Documentation](https://api.ilert.com/api-docs/)
- [ILert Incident Management](https://www.ilert.com/incident-management/)
23 changes: 23 additions & 0 deletions examples/workflows/ilert-incident-upon-alert.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
id: aad72d69-92b9-4e21-8f67-97d2a69bf8ac
description: Create ILert incident upon Keep Alert
triggers:
- filters:
- key: source
value: keep
type: alert
owners: []
services: []
steps: []
actions:
- name: ilert-action
provider:
config: '{{ providers.ilert-default }}'
type: ilert
with:
affectedServices:
- impact: OPERATIONAL
service:
id: 339743
message: A mock incident created with Keep!
status: INVESTIGATING
summary: Keep Incident {{ alert.name }}
3 changes: 3 additions & 0 deletions keep-ui/app/alerts/alerts.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
div[role="tooltip"] {
max-width: 28rem !important;
}
3 changes: 2 additions & 1 deletion keep-ui/app/alerts/alerts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import AlertActions from "./alert-actions";
import { RowSelectionState } from "@tanstack/react-table";
import { GlobeIcon } from "@radix-ui/react-icons";
import { getAlertLastReceieved } from "utils/helpers";
import "./alerts.css";

const defaultPresets: Preset[] = [
{ name: "Feed", options: [] },
Expand Down Expand Up @@ -351,7 +352,7 @@ export default function Alerts({
tooltip="Live alerts are streamlining from Keep"
size="xs"
>
 Live
<span className="animate-ping">&nbsp;Live</span>
</Badge>
<Subtitle className="text-[10px]">
Last received:{" "}
Expand Down
Binary file added keep-ui/public/icons/ilert-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
246 changes: 246 additions & 0 deletions keep/providers/ilert_provider/ilert_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
"""
Ilert Provider is a class that allows to create/close incidents in Ilert.
"""
import dataclasses
import enum
import json
import os

import pydantic
import requests

from keep.contextmanager.contextmanager import ContextManager
from keep.providers.base.base_provider import BaseProvider
from keep.providers.models.provider_config import ProviderConfig, ProviderScope
from keep.providers.providers_factory import ProvidersFactory


class IlertIncidentStatus(str, enum.Enum):
"""
Ilert incident status.
"""

INVESTIGATING = "INVESTIGATING"
RESOLVED = "RESOLVED"
MONITORING = "MONITORING"
IDENTIFIED = "IDENTIFIED"


class IlertServiceStatus(str, enum.Enum):
"""
Ilert service status.
"""

OPERATIONAL = "OPERATIONAL"
DEGRADED = "DEGRADED"
PARTIAL_OUTAGE = "PARTIAL_OUTAGE"
MAJOR_OUTAGE = "MAJOR_OUTAGE"
UNDER_MAINTENANCE = "UNDER_MAINTENANCE"


class IlertServiceNoIncludes(pydantic.BaseModel):
"""
Ilert service.
"""

id: str


class IlertAffectedService(pydantic.BaseModel):
"""
Ilert affected service.
"""

service: IlertServiceNoIncludes
impact: IlertServiceStatus


@pydantic.dataclasses.dataclass
class IlertProviderAuthConfig:
"""
Ilert authentication configuration.
"""

ilert_token: str = dataclasses.field(
metadata={
"required": True,
"description": "ILert API token",
"hint": "Bearer eyJhbGc...",
"sensitive": True,
}
)
ilert_host: str = dataclasses.field(
metadata={
"required": False,
"description": "ILert API host",
"hint": "https://api.ilert.com/api",
},
default="https://api.ilert.com/api",
)


class IlertProvider(BaseProvider):
"""Create/Resolve incidents in Ilert."""

PROVIDER_SCOPES = [
ProviderScope("read_permission", "Read permission", mandatory=True),
ProviderScope("write_permission", "Write permission", mandatory=False),
]

def __init__(
self, context_manager: ContextManager, provider_id: str, config: ProviderConfig
):
super().__init__(context_manager, provider_id, config)

def dispose(self):
"""
Dispose the provider.
"""
pass

def validate_config(self):
"""
Validates required configuration for Ilert provider.
"""
self.authentication_config = IlertProviderAuthConfig(
**self.config.authentication
)

def validate_scopes(self):
scopes = {}
self.logger.info("Validating scopes")
for scope in self.PROVIDER_SCOPES:
try:
if scope.name == "read_permission":
requests.get(
f"{self.authentication_config.ilert_host}/incidents",
headers={
"Authorization": self.authentication_config.ilert_token
},
)
scopes[scope.name] = True
elif scope.name == "write_permission":
# TODO: find a way to validate write_permissions, for now it is always "validated" sucessfully.
scopes[scope.name] = True
except Exception as e:
self.logger.warning(
"Failed to validate scope",
extra={"scope": scope.name},
)
scopes[scope.name] = str(e)
self.logger.info("Scopes validated", extra=scopes)
return scopes

def _notify(
self,
summary: str,
status: IlertIncidentStatus = IlertIncidentStatus.INVESTIGATING,
message: str = "",
affectedServices: str | list = "[]",
id: str = "0",
**kwargs: dict,
):
self.logger.info(
"Creating/updating Ilert incident",
extra={
"summary": summary,
"status": status,
"incident_message": message,
"affectedServices": affectedServices,
"id": id,
},
)
headers = {"Authorization": self.authentication_config.ilert_token}

# Create or update incident
payload = {
"id": id,
"summary": summary,
"status": str(status),
"message": message,
**kwargs,
}
if affectedServices:
try:
payload["affectedServices"] = (
json.loads(affectedServices)
if isinstance(affectedServices, str)
else affectedServices
)
except Exception:
self.logger.warning(
"Failed to parse affectedServices",
extra={"affectedServices": affectedServices},
)

# if id is set, we update the incident, otherwise we create a new one
should_update = id and id != "0"
if not should_update:
response = requests.post(
f"{self.authentication_config.ilert_host}/incidents",
headers=headers,
json=payload,
)
else:
response = requests.put(
f"{self.authentication_config.ilert_host}/incidents/{id}",
headers=headers,
json=payload,
)

if not response.ok:
self.logger.error(
"Failed to create/update Ilert incident",
extra={
"status_code": response.status_code,
"response": response.text,
},
)
raise Exception(
f"Failed to create/update Ilert incident: {response.status_code} {response.text}"
)
self.logger.info(
"Ilert incident created/updated",
extra={"status_code": response.status_code},
)


if __name__ == "__main__":
# Output debug messages
import logging

logging.basicConfig(level=logging.DEBUG, handlers=[logging.StreamHandler()])
context_manager = ContextManager(
tenant_id="singletenant",
workflow_id="test",
)
# Load environment variables
import os

api_key = os.environ.get("ILERT_API_TOKEN")

provider_config = {
"authentication": {"ilert_token": api_key},
}
provider: IlertProvider = ProvidersFactory.get_provider(
context_manager=context_manager,
provider_id="ilert",
provider_type="ilert",
provider_config=provider_config,
)
result = provider._query(
"Example",
message="Lorem Ipsum",
status="MONITORING",
affectedServices=json.dumps(
[
{
"impact": "OPERATIONAL",
"service": {"id": 339743},
}
]
),
id="242530",
)
print(result)
1 change: 1 addition & 0 deletions keep/rulesengine/rulesengine.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ def run_rules(self, events: list[AlertDto]):
"id": fingerprint,
"status": "firing",
"pushed": True,
"fingerprint": fingerprint,
},
fingerprint=fingerprint,
)
Expand Down
Loading

0 comments on commit af748b5

Please sign in to comment.