Skip to content

Commit

Permalink
feat(provider): ilert (#689)
Browse files Browse the repository at this point in the history
  • Loading branch information
talboren authored Jan 7, 2024
1 parent 4796fa1 commit b8f0a18
Show file tree
Hide file tree
Showing 6 changed files with 318 additions and 0 deletions.
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 }}
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.
Empty file.
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 comment on commit b8f0a18

@vercel
Copy link

@vercel vercel bot commented on b8f0a18 Jan 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

keep – ./

keep-eight.vercel.app
keep-keephq.vercel.app
keep-git-main-keephq.vercel.app
platform.keephq.dev

Please sign in to comment.