Skip to content

feat: enhance provider and deduplication rule provisioning logic #4399

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 32 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
d39be4c
feat: enhance deduplication rules provisioning with provider support …
tuantran0910 Apr 3, 2025
4fa513d
test: add unit tests for ProvidersService hash handling with Redis an…
tuantran0910 Apr 4, 2025
a14edd1
test: add more unit tests for ProvidersService hash handling with Red…
tuantran0910 Apr 4, 2025
0e05ce6
fix: update log message for reading provisioned hash from file in Pro…
tuantran0910 Apr 4, 2025
fc2fdd2
refactor: remove redundant deletion of non-existent deduplication rul…
tuantran0910 Apr 4, 2025
5c77215
Merge branch 'main' into refactor-provider-provisioning-logic
talboren Apr 4, 2025
08df785
Merge branch 'main' into refactor-provider-provisioning-logic
tuantran0910 Apr 4, 2025
3bc9abf
Merge branch 'main' into refactor-provider-provisioning-logic
tuantran0910 Apr 4, 2025
3ecf11d
refactor: update ProvidersService to use secret manager for hash valu…
tuantran0910 Apr 5, 2025
9aea113
Merge branch 'main' into refactor-provider-provisioning-logic
tuantran0910 Apr 5, 2025
a4b3731
docs: update provisioning documentation
tuantran0910 Apr 5, 2025
add8374
test: update deduplication rules and adjust provisioning tests
tuantran0910 Apr 5, 2025
7be8b47
test: fix KEEP_PROVIDERS configuration for VictoriaMetrics in provisi…
tuantran0910 Apr 5, 2025
301a9a9
refactor: simplify session management in ProvidersService by using ex…
tuantran0910 Apr 6, 2025
0b78a13
Merge branch 'main' into refactor-provider-provisioning-logic
tuantran0910 Apr 6, 2025
bfbf25c
Merge branch 'main' into refactor-provider-provisioning-logic
shahargl Apr 7, 2025
911d89a
Merge branch 'main' into refactor-provider-provisioning-logic
tuantran0910 Apr 7, 2025
7631098
Merge branch 'main' into refactor-provider-provisioning-logic
tuantran0910 Apr 10, 2025
4b33436
refactor: alter logic to provision providers
tuantran0910 Apr 12, 2025
46b57d0
Merge branch 'main' into refactor-provider-provisioning-logic
tuantran0910 Apr 12, 2025
68e4cdf
refactor: enhance deduplication rules provisioning logic
tuantran0910 Apr 12, 2025
456544d
test: add unit tests for provider deletion and deduplication rule han…
tuantran0910 Apr 12, 2025
c149182
feat: add validate_scopes parameter to ProvidersService and improve d…
tuantran0910 Apr 12, 2025
8ca1f2c
refactor: streamline deduplication rules provisioning and improve log…
tuantran0910 Apr 12, 2025
22620e8
delete: remove obsolete unit tests for provider deletion and deduplic…
tuantran0910 Apr 12, 2025
5c6ba43
refactor: update provider provisioning logic to improve configuration…
tuantran0910 Apr 12, 2025
012e14b
fix: update KEEP_PROVIDERS key in deduplication tests for consistency
tuantran0910 Apr 12, 2025
56fc571
Merge branch 'main' into refactor-provider-provisioning-logic
talboren Apr 14, 2025
388e22c
Merge branch 'main' into refactor-provider-provisioning-logic
tuantran0910 Apr 14, 2025
951aa42
Merge branch 'main' into refactor-provider-provisioning-logic
tuantran0910 Apr 15, 2025
94b5c3e
Merge branch 'main' into refactor-provider-provisioning-logic
tuantran0910 Apr 20, 2025
dfb7b70
Merge branch 'main' into refactor-provider-provisioning-logic
tuantran0910 Apr 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/deployment/provision/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ Provisioning in Keep is controlled through environment variables and configurati
| Provisioning Type | Environment Variable | Purpose |
| ---------------------- | ------------------------------ | ------------------------------------------------------------------------- |
| **Provider** | `KEEP_PROVIDERS` | JSON string containing provider configurations with deduplication rules |
| **Workflow** | `KEEP_WORKFLOW` | One workflow to provision right from the env variable. |
| **Providers** | `KEEP_PROVIDERS_DIRECTORY` | Directory path containing provider configuration files |
| **Workflow** | `KEEP_WORKFLOW` | One workflow to provision right from the env variable |
| **Workflows** | `KEEP_WORKFLOWS_DIRECTORY` | Directory path containing workflow configuration files |
| **Dashboard** | `KEEP_DASHBOARDS` | JSON string containing dashboard configurations |

Expand Down
28 changes: 7 additions & 21 deletions docs/deployment/provision/provider.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -99,29 +99,15 @@ deduplication_rules:
Keep supports a wide range of provider types. Each provider type has its own specific configuration requirements.
To see the full list of supported providers and their detailed configuration options, please refer to our comprehensive provider documentation.


### Update Provisioned Providers

#### Using KEEP_PROVIDERS

Provider configurations can be updated dynamically by changing the `KEEP_PROVIDERS` environment variable.

On every restart, Keep reads this environment variable and determines which providers need to be added or removed.

This process allows for flexible management of data sources without requiring manual intervention. By simply updating the `KEEP_PROVIDERS` variable and restarting the application, you can efficiently add new providers, remove existing ones, or modify their configurations.

The high-level provisioning mechanism:
1. Keep reads the `KEEP_PROVIDERS` value.
2. Keep checks if there are any provisioned providers that are no longer in the `KEEP_PROVIDERS` value, and deletes them.
3. Keep installs all providers from the `KEEP_PROVIDERS` value.

#### Using KEEP_PROVIDERS_DIRECTORY
Keep uses a consistent process for updating provider configurations regardless of whether you use `KEEP_PROVIDERS` or `KEEP_PROVIDERS_DIRECTORY`.

Provider configurations can be updated dynamically by changing the YAML files in the `KEEP_PROVIDERS_DIRECTORY` directory.
#### Provisioning Process

On every restart, Keep reads the YAML files in the `KEEP_PROVIDERS_DIRECTORY` directory and determines which providers need to be added or removed.
When Keep starts or restarts, it follows these steps to manage provider configurations:

The high-level provisioning mechanism:
1. Keep reads the YAML files in the `KEEP_PROVIDERS_DIRECTORY` directory.
2. Keep checks if there are any provisioned providers that are no longer in the YAML files, and deletes them.
3. Keep installs all providers from the YAML files.
1. **Read Configurations**: Loads provider definitions from either the `KEEP_PROVIDERS` environment variable or YAML files in the `KEEP_PROVIDERS_DIRECTORY`.
2. **Create New Providers**: Installs any providers listed in the configuration that are not already present.
3. **Update When Changed**: If an existing provider's configuration has changed, Keep reapplies the configuration, including deduplication rules. If errors occur during this update, changes are automatically rolled back.
4. **Delete Providers**: Deletes any currently installed providers that are not found in the loaded configuration.
73 changes: 28 additions & 45 deletions keep/api/alert_deduplicator/deduplication_rules_provisioning.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,28 @@

import keep.api.core.db as db
from keep.api.core.config import config
from keep.providers.providers_factory import ProvidersFactory
from keep.api.models.db.provider import Provider

logger = logging.getLogger(__name__)


def provision_deduplication_rules(deduplication_rules: dict[str, any], tenant_id: str):
def provision_deduplication_rules(
deduplication_rules: dict[str, any], tenant_id: str, provider: Provider
):
"""
Provisions deduplication rules for a given tenant.

Args:
deduplication_rules (dict[str, any]): A dictionary where the keys are rule names and the values are
DeduplicationRuleRequestDto objects.
DeduplicationRuleRequestDto objects.
tenant_id (str): The ID of the tenant for which deduplication rules are being provisioned.
provider (Provider): The provider for which the deduplication rules are being provisioned.
"""
enrich_with_providers_info(deduplication_rules, tenant_id)
enrich_with_provider_info(deduplication_rules, provider)

Check warning on line 24 in keep/api/alert_deduplicator/deduplication_rules_provisioning.py

View check run for this annotation

Codecov / codecov/patch

keep/api/alert_deduplicator/deduplication_rules_provisioning.py#L24

Added line #L24 was not covered by tests

all_deduplication_rules_from_db = db.get_all_deduplication_rules(tenant_id)
all_deduplication_rules_from_db = db.get_all_deduplication_rules_by_provider(

Check warning on line 26 in keep/api/alert_deduplicator/deduplication_rules_provisioning.py

View check run for this annotation

Codecov / codecov/patch

keep/api/alert_deduplicator/deduplication_rules_provisioning.py#L26

Added line #L26 was not covered by tests
tenant_id, provider.id, provider.type
)
provisioned_deduplication_rules = [
rule for rule in all_deduplication_rules_from_db if rule.is_provisioned
]
Expand All @@ -29,17 +34,6 @@
}
actor = "system"

# delete rules that are not in the env
for provisioned_deduplication_rule in provisioned_deduplication_rules:
if str(provisioned_deduplication_rule.name) not in deduplication_rules:
logger.info(
"Deduplication rule with name '%s' is not in the env, deleting from DB",
provisioned_deduplication_rule.name,
)
db.delete_deduplication_rule(
rule_id=str(provisioned_deduplication_rule.id), tenant_id=tenant_id
)

for (
deduplication_rule_name,
deduplication_rule_to_provision,
Expand Down Expand Up @@ -97,47 +91,36 @@
is_provisioned=True,
)

logger.info(

Check warning on line 94 in keep/api/alert_deduplicator/deduplication_rules_provisioning.py

View check run for this annotation

Codecov / codecov/patch

keep/api/alert_deduplicator/deduplication_rules_provisioning.py#L94

Added line #L94 was not covered by tests
"Provisioned deduplication rules %s successfully",
deduplication_rule_name,
)

def provision_deduplication_rules_from_env(tenant_id: str):
"""
Provisions deduplication rules from environment variables for a given tenant.
This function reads deduplication rules from environment variables, validates them,
and then provisions them into the database. It handles the following:
- Deletes deduplication rules from the database that are not present in the environment variables.
- Updates existing deduplication rules in the database if they are present in the environment variables.
- Creates new deduplication rules in the database if they are not already present.
Args:
tenant_id (str): The ID of the tenant for which deduplication rules are being provisioned.
Raises:
ValueError: If the deduplication rules from the environment variables are invalid.
"""

deduplication_rules_from_env_dict = get_deduplication_rules_to_provision()

if not deduplication_rules_from_env_dict:
logger.info("No deduplication rules found in env. Nothing to provision.")
return

provision_deduplication_rules(deduplication_rules_from_env_dict, tenant_id)
# Delete provisioned deduplication rules that are not provisioned anymore
for rule_name, rule in provisioned_deduplication_rules_from_db_dict.items():
if rule_name not in deduplication_rules:
logger.info(

Check warning on line 102 in keep/api/alert_deduplicator/deduplication_rules_provisioning.py

View check run for this annotation

Codecov / codecov/patch

keep/api/alert_deduplicator/deduplication_rules_provisioning.py#L100-L102

Added lines #L100 - L102 were not covered by tests
"Deduplication rule with name '%s' no longer in configuration, deleting from DB",
rule_name,
)
db.delete_deduplication_rule(rule_id=str(rule.id), tenant_id=tenant_id)
logger.info(

Check warning on line 107 in keep/api/alert_deduplicator/deduplication_rules_provisioning.py

View check run for this annotation

Codecov / codecov/patch

keep/api/alert_deduplicator/deduplication_rules_provisioning.py#L106-L107

Added lines #L106 - L107 were not covered by tests
"Deleted deduplication rule %s successfully",
rule_name,
)


def enrich_with_providers_info(deduplication_rules: dict[str, any], tenant_id: str):
def enrich_with_provider_info(deduplication_rules: dict[str, any], provider: Provider):
"""
Enriches passed deduplication rules with provider ID and type information.

Args:
deduplication_rules (dict[str, any]): A list of deduplication rules to be enriched.
tenant_id (str): The ID of the tenant for which deduplication rules are being provisioned.
provider (Provider): The provider for which the deduplication rules are being provisioned.
"""

installed_providers = ProvidersFactory.get_installed_providers(tenant_id)
installed_providers_dict = {
provider.details.get("name"): provider for provider in installed_providers
}

for rule_name, rule in deduplication_rules.items():
logger.info(f"Enriching deduplication rule: {rule_name}")
provider = installed_providers_dict.get(rule.get("provider_name"))
rule["provider_id"] = provider.id
rule["provider_type"] = provider.type

Expand Down
6 changes: 0 additions & 6 deletions keep/api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
import os

import keep.api.logging
from keep.api.alert_deduplicator.deduplication_rules_provisioning import (
provision_deduplication_rules_from_env,
)
from keep.api.api import AUTH_TYPE
from keep.api.core.db_on_start import migrate_db, try_create_single_tenant
from keep.api.core.dependencies import SINGLE_TENANT_UUID
Expand Down Expand Up @@ -33,9 +30,6 @@ def provision_resources():
logger.info("Workflows provisioned successfully")
provision_dashboards(SINGLE_TENANT_UUID)
logger.info("Dashboards provisioned successfully")
logger.info("Provisioning deduplication rules")
provision_deduplication_rules_from_env(SINGLE_TENANT_UUID)
logger.info("Deduplication rules provisioned successfully")
else:
logger.info("Provisioning resources is disabled")

Expand Down
14 changes: 13 additions & 1 deletion keep/api/core/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
from sqlalchemy.dialects.postgresql import insert as pg_insert
from sqlalchemy.dialects.sqlite import insert as sqlite_insert
from sqlalchemy.exc import IntegrityError, OperationalError
from sqlalchemy.orm import joinedload, subqueryload, foreign
from sqlalchemy.orm import foreign, joinedload, subqueryload
from sqlalchemy.orm.exc import StaleDataError
from sqlalchemy.sql import exists, expression
from sqlmodel import Session, SQLModel, col, or_, select, text
Expand Down Expand Up @@ -2354,6 +2354,18 @@
return rules


def get_all_deduplication_rules_by_provider(tenant_id, provider_id, provider_type):
with Session(engine) as session:
rules = session.exec(

Check warning on line 2359 in keep/api/core/db.py

View check run for this annotation

Codecov / codecov/patch

keep/api/core/db.py#L2358-L2359

Added lines #L2358 - L2359 were not covered by tests
select(AlertDeduplicationRule).where(
AlertDeduplicationRule.tenant_id == tenant_id,
AlertDeduplicationRule.provider_id == provider_id,
AlertDeduplicationRule.provider_type == provider_type,
)
).all()
return rules

Check warning on line 2366 in keep/api/core/db.py

View check run for this annotation

Codecov / codecov/patch

keep/api/core/db.py#L2366

Added line #L2366 was not covered by tests


def get_deduplication_rule_by_id(tenant_id, rule_id: str):
rule_uuid = __convert_to_uuid(rule_id)
if not rule_uuid:
Expand Down
Loading
Loading