Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 26 additions & 0 deletions config/constants/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,22 @@
USAGE_SECRET_ENV,
WEBAPP_URL_ENV,
)
from config.constants.coralogix import (
CORALOGIX_API_KEY_ENV,
CORALOGIX_APPLICATION_NAME_ENV,
CORALOGIX_BASE_URL_ENV,
CORALOGIX_SUBSYSTEM_NAME_ENV,
)
from config.constants.datadog import (
DATADOG_API_KEY_ENV,
DATADOG_APP_KEY_ENV,
DATADOG_SITE_ENV,
)
from config.constants.honeycomb import (
HONEYCOMB_API_KEY_ENV,
HONEYCOMB_BASE_URL_ENV,
HONEYCOMB_DATASET_ENV,
)
from config.constants.investigation import MAX_INVESTIGATION_LOOPS
from config.constants.llm import (
AZURE_OPENAI_API_KEY_ENV,
Expand Down Expand Up @@ -44,9 +60,19 @@
"AZURE_OPENAI_API_KEY_ENV",
"AZURE_OPENAI_API_VERSION_ENV",
"AZURE_OPENAI_BASE_URL_ENV",
"CORALOGIX_API_KEY_ENV",
"CORALOGIX_APPLICATION_NAME_ENV",
"CORALOGIX_BASE_URL_ENV",
"CORALOGIX_SUBSYSTEM_NAME_ENV",
"CREDITS_HTTP_TIMEOUT_SECONDS",
"DATADOG_API_KEY_ENV",
"DATADOG_APP_KEY_ENV",
"DATADOG_SITE_ENV",
"DEFAULT_POSTHOG_TIMEOUT_SECONDS",
"DEFAULT_POSTHOG_URL",
"HONEYCOMB_API_KEY_ENV",
"HONEYCOMB_BASE_URL_ENV",
"HONEYCOMB_DATASET_ENV",
"INTEGRATIONS_STORE_PATH",
"IS_WINDOWS",
"MAX_INVESTIGATION_LOOPS",
Expand Down
16 changes: 16 additions & 0 deletions config/constants/coralogix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""Coralogix environment variable names."""

from __future__ import annotations

CORALOGIX_API_KEY_ENV = "CORALOGIX_API_KEY"
# Mirrors the ``base_url`` credential; the names deliberately differ.
CORALOGIX_BASE_URL_ENV = "CORALOGIX_API_URL"
CORALOGIX_APPLICATION_NAME_ENV = "CORALOGIX_APPLICATION_NAME"
CORALOGIX_SUBSYSTEM_NAME_ENV = "CORALOGIX_SUBSYSTEM_NAME"

__all__ = [
"CORALOGIX_API_KEY_ENV",
"CORALOGIX_APPLICATION_NAME_ENV",
"CORALOGIX_BASE_URL_ENV",
"CORALOGIX_SUBSYSTEM_NAME_ENV",
]
13 changes: 13 additions & 0 deletions config/constants/datadog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""Datadog environment variable names."""

from __future__ import annotations

DATADOG_API_KEY_ENV = "DD_API_KEY"
DATADOG_APP_KEY_ENV = "DD_APP_KEY"
DATADOG_SITE_ENV = "DD_SITE"

__all__ = [
"DATADOG_API_KEY_ENV",
"DATADOG_APP_KEY_ENV",
"DATADOG_SITE_ENV",
]
14 changes: 14 additions & 0 deletions config/constants/honeycomb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""Honeycomb environment variable names."""

from __future__ import annotations

HONEYCOMB_API_KEY_ENV = "HONEYCOMB_API_KEY"
HONEYCOMB_DATASET_ENV = "HONEYCOMB_DATASET"
# Mirrors the ``base_url`` credential; the names deliberately differ.
HONEYCOMB_BASE_URL_ENV = "HONEYCOMB_API_URL"

__all__ = [
"HONEYCOMB_API_KEY_ENV",
"HONEYCOMB_BASE_URL_ENV",
"HONEYCOMB_DATASET_ENV",
]
39 changes: 29 additions & 10 deletions integrations/_catalog_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@
from typing import Any

from config.config import get_tracer_base_url
from config.constants.coralogix import (
CORALOGIX_API_KEY_ENV,
CORALOGIX_APPLICATION_NAME_ENV,
CORALOGIX_BASE_URL_ENV,
CORALOGIX_SUBSYSTEM_NAME_ENV,
)
from config.constants.datadog import (
DATADOG_API_KEY_ENV,
DATADOG_APP_KEY_ENV,
DATADOG_SITE_ENV,
)
from config.constants.honeycomb import (
HONEYCOMB_API_KEY_ENV,
HONEYCOMB_BASE_URL_ENV,
HONEYCOMB_DATASET_ENV,
)
from config.llm_credentials import resolve_env_credential
from integrations.airflow.config import airflow_config_from_env
from integrations.airflow.config import classify as _classify_airflow
Expand All @@ -22,6 +38,7 @@
from integrations.betterstack import classify as _classify_betterstack
from integrations.bitbucket import classify as _classify_bitbucket
from integrations.config_models import (
DEFAULT_DATADOG_SITE,
AlertmanagerIntegrationConfig,
ArgoCDIntegrationConfig,
AWSIntegrationConfig,
Expand Down Expand Up @@ -434,9 +451,11 @@ def load_env_integrations() -> list[dict[str, Any]]:
datadog_app_key = ""
datadog_site = ""
else:
datadog_api_key = resolve_env_credential("DD_API_KEY")
datadog_app_key = resolve_env_credential("DD_APP_KEY")
datadog_site = os.getenv("DD_SITE", "datadoghq.com").strip() or "datadoghq.com"
datadog_api_key = resolve_env_credential(DATADOG_API_KEY_ENV)
datadog_app_key = resolve_env_credential(DATADOG_APP_KEY_ENV)
datadog_site = (
os.getenv(DATADOG_SITE_ENV, DEFAULT_DATADOG_SITE).strip() or DEFAULT_DATADOG_SITE
)
if datadog_api_key and datadog_app_key:
try:
datadog_config = DatadogIntegrationConfig.model_validate(
Expand Down Expand Up @@ -493,14 +512,14 @@ def load_env_integrations() -> list[dict[str, Any]]:
integrations.append(honeycomb_multi)
honeycomb_api_key = ""
else:
honeycomb_api_key = resolve_env_credential("HONEYCOMB_API_KEY")
honeycomb_api_key = resolve_env_credential(HONEYCOMB_API_KEY_ENV)
if honeycomb_api_key:
try:
honeycomb_config = HoneycombIntegrationConfig.model_validate(
{
"api_key": honeycomb_api_key,
"dataset": os.getenv("HONEYCOMB_DATASET", "").strip(),
"base_url": os.getenv("HONEYCOMB_API_URL", "").strip(),
"dataset": os.getenv(HONEYCOMB_DATASET_ENV, "").strip(),
"base_url": os.getenv(HONEYCOMB_BASE_URL_ENV, "").strip(),
}
)
except Exception as exc:
Expand All @@ -518,15 +537,15 @@ def load_env_integrations() -> list[dict[str, Any]]:
integrations.append(coralogix_multi)
coralogix_api_key = ""
else:
coralogix_api_key = resolve_env_credential("CORALOGIX_API_KEY")
coralogix_api_key = resolve_env_credential(CORALOGIX_API_KEY_ENV)
if coralogix_api_key:
try:
coralogix_config = CoralogixIntegrationConfig.model_validate(
{
"api_key": coralogix_api_key,
"base_url": os.getenv("CORALOGIX_API_URL", "").strip(),
"application_name": os.getenv("CORALOGIX_APPLICATION_NAME", "").strip(),
"subsystem_name": os.getenv("CORALOGIX_SUBSYSTEM_NAME", "").strip(),
"base_url": os.getenv(CORALOGIX_BASE_URL_ENV, "").strip(),
"application_name": os.getenv(CORALOGIX_APPLICATION_NAME_ENV, "").strip(),
"subsystem_name": os.getenv(CORALOGIX_SUBSYSTEM_NAME_ENV, "").strip(),
}
)
except Exception as exc:
Expand Down
49 changes: 13 additions & 36 deletions integrations/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,14 +193,9 @@ def _setup_grafana() -> None:


def _setup_datadog() -> None:
api_key = _p("API key", secret=True)
app_key = _p("Application key", secret=True)
site = _p("Site", default="datadoghq.com")
if not api_key or not app_key:
_die("api_key and app_key are required.")
upsert_integration(
"datadog", {"credentials": {"api_key": api_key, "app_key": app_key, "site": site}}
)
from integrations.datadog.setup import DATADOG_SETUP

_run_spec_setup(DATADOG_SETUP)


def _setup_groundcover() -> None:
Expand All @@ -224,35 +219,15 @@ def _setup_groundcover() -> None:


def _setup_honeycomb() -> None:
api_key = _p("Configuration API key", secret=True)
dataset = _p("Dataset slug or __all__", default="__all__")
base_url = _p("API URL", default="https://api.honeycomb.io")
if not api_key:
_die("api_key is required.")
upsert_integration(
"honeycomb",
{"credentials": {"api_key": api_key, "dataset": dataset, "base_url": base_url}},
)
from integrations.honeycomb.setup import HONEYCOMB_SETUP

_run_spec_setup(HONEYCOMB_SETUP)


def _setup_coralogix() -> None:
api_key = _p("DataPrime API key", secret=True)
base_url = _p("API URL", default="https://api.coralogix.com")
application_name = _p("Application name (optional)")
subsystem_name = _p("Subsystem name (optional)")
if not api_key or not base_url:
_die("api_key and base_url are required.")
upsert_integration(
"coralogix",
{
"credentials": {
"api_key": api_key,
"base_url": base_url,
"application_name": application_name,
"subsystem_name": subsystem_name,
}
},
)
from integrations.coralogix.setup import CORALOGIX_SETUP

_run_spec_setup(CORALOGIX_SETUP)


def _setup_aws() -> None:
Expand Down Expand Up @@ -880,8 +855,10 @@ def _run_spec_setup(spec: IntegrationSetupSpec) -> None:

values: dict[str, str | None] = {}
for field in spec.fields:
value = _p(field.question, secret=field.secret)
if not value and field.required:
value = _p(field.question, default=field.default, secret=field.secret)
# A field with a default is never missing — apply_setup substitutes it —
# so only a defaultless required field can fail here.
if not value and field.required and not field.default:
_die(f"{field.label} is required.")
values[field.name] = value

Expand Down
5 changes: 3 additions & 2 deletions integrations/config_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
_LOCAL_GRAFANA_HOSTS = {"localhost", "127.0.0.1", "0.0.0.0"}
DEFAULT_GROUNDCOVER_MCP_URL = "https://mcp.groundcover.com/api/mcp"
DEFAULT_GROUNDCOVER_TIMEZONE = "UTC"
DEFAULT_DATADOG_SITE = "datadoghq.com"
DEFAULT_HONEYCOMB_BASE_URL = "https://api.honeycomb.io"
DEFAULT_HONEYCOMB_DATASET = "__all__"
DEFAULT_CORALOGIX_BASE_URL = "https://api.coralogix.com"
Expand Down Expand Up @@ -122,11 +123,11 @@ class DatadogIntegrationConfig(StrictConfigModel):

api_key: str
app_key: str
site: str = "datadoghq.com"
site: str = DEFAULT_DATADOG_SITE
integration_id: str = ""

_normalize_site = field_validator("site", mode="before")(
normalize_with_default("datadoghq.com")
normalize_with_default(DEFAULT_DATADOG_SITE)
)

@property
Expand Down
66 changes: 66 additions & 0 deletions integrations/coralogix/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""What Coralogix needs before it is considered configured.

Only the DataPrime key is required. ``base_url`` is regional and defaults to the
US host. The application and subsystem names are optional query filters — left
blank, queries span the whole account.
"""

from __future__ import annotations

from config.constants.coralogix import (
CORALOGIX_API_KEY_ENV,
CORALOGIX_APPLICATION_NAME_ENV,
CORALOGIX_BASE_URL_ENV,
CORALOGIX_SUBSYSTEM_NAME_ENV,
)
from integrations.config_models import DEFAULT_CORALOGIX_BASE_URL
from integrations.coralogix.verifier import verify_coralogix
from integrations.setup_flow import IntegrationSetupSpec, SetupField

API_KEY_FIELD = "api_key"
BASE_URL_FIELD = "base_url"
APPLICATION_NAME_FIELD = "application_name"
SUBSYSTEM_NAME_FIELD = "subsystem_name"

CORALOGIX_SETUP = IntegrationSetupSpec(
service="coralogix",
fields=(
SetupField(
name=API_KEY_FIELD,
label="Coralogix API key",
prompt="DataPrime API key",
env_var=CORALOGIX_API_KEY_ENV,
secret=True,
),
SetupField(
name=BASE_URL_FIELD,
label="Coralogix API URL",
prompt="API URL",
env_var=CORALOGIX_BASE_URL_ENV,
default=DEFAULT_CORALOGIX_BASE_URL,
),
SetupField(
name=APPLICATION_NAME_FIELD,
label="Coralogix application name",
prompt="Application name (optional)",
env_var=CORALOGIX_APPLICATION_NAME_ENV,
required=False,
),
SetupField(
name=SUBSYSTEM_NAME_FIELD,
label="Coralogix subsystem name",
prompt="Subsystem name (optional)",
env_var=CORALOGIX_SUBSYSTEM_NAME_ENV,
required=False,
),
),
verify=verify_coralogix,
)

__all__ = [
"API_KEY_FIELD",
"APPLICATION_NAME_FIELD",
"BASE_URL_FIELD",
"CORALOGIX_SETUP",
"SUBSYSTEM_NAME_FIELD",
]
58 changes: 58 additions & 0 deletions integrations/datadog/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""What Datadog needs before it is considered configured.

Both keys are required and neither substitutes for the other: the API key
authenticates ingestion, the application key authorizes the read endpoints every
Datadog tool calls. A setup with only one verifies against nothing useful.

``site`` decides which regional API host is contacted, so a US-default account
in the EU fails every query with a confusing 403 rather than a routing error.
It is prompted with a default rather than inferred.
"""

from __future__ import annotations

from config.constants.datadog import (
DATADOG_API_KEY_ENV,
DATADOG_APP_KEY_ENV,
DATADOG_SITE_ENV,
)
from integrations.config_models import DEFAULT_DATADOG_SITE
from integrations.datadog.verifier import verify_datadog
from integrations.setup_flow import IntegrationSetupSpec, SetupField

API_KEY_FIELD = "api_key"
APP_KEY_FIELD = "app_key"
SITE_FIELD = "site"

DATADOG_SETUP = IntegrationSetupSpec(
service="datadog",
fields=(
SetupField(
name=API_KEY_FIELD,
label="Datadog API key",
env_var=DATADOG_API_KEY_ENV,
secret=True,
),
SetupField(
name=APP_KEY_FIELD,
label="Datadog application key",
env_var=DATADOG_APP_KEY_ENV,
secret=True,
),
SetupField(
name=SITE_FIELD,
label="Datadog site",
prompt="Site (e.g. datadoghq.com, datadoghq.eu)",
env_var=DATADOG_SITE_ENV,
default=DEFAULT_DATADOG_SITE,
),
),
verify=verify_datadog,
)

__all__ = [
"API_KEY_FIELD",
"APP_KEY_FIELD",
"DATADOG_SETUP",
"SITE_FIELD",
]
Loading