Skip to content

Commit

Permalink
Merge branch 'holmes-docs-enable-custom-tools' of github.com:robusta-…
Browse files Browse the repository at this point in the history
…dev/robusta into holmes-docs-enable-custom-tools
  • Loading branch information
itisallgood committed Dec 15, 2024
2 parents afd8e09 + d2d903a commit 8f2e664
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 22 deletions.
14 changes: 14 additions & 0 deletions docs/configuration/sinks/jira.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ Prerequisites
Optional Settings
---------------------------
* ``issue_type`` : [Optional - default: ``Task``] Jira ticket type
* ``priority_mapping`` : [Optional] Maps Robusta severity levels to Jira priorities. Example:
.. code-block:: yaml
priority_mapping:
HIGH: "High"
MEDIUM: "Medium"
LOW: "Low"
INFO: "Lowest"
* ``dedups`` : [Optional - default: ``fingerprint``] Tickets deduplication parameter. By default, Only one issue per ``fingerprint`` will be created. There can be more than one value to use. Possible values are: fingerprint, cluster_name, title, node, type, source, namespace, creation_date etc
* ``project_type_id_override`` : [Optional - default: None] If available, will override the ``project_name`` configuration. Follow these `instructions <https://confluence.atlassian.com/jirakb/how-to-get-project-id-from-the-jira-user-interface-827341414.html>`__ to get your project id.
* ``issue_type_id_override`` : [Optional - default: None] If available, will override the ``issue_type`` configuration. Follow these `instructions <https://confluence.atlassian.com/jirakb/finding-the-id-for-issue-types-646186508.html>`__ to get your issue id.
Expand Down Expand Up @@ -59,6 +68,11 @@ Configuring the Jira sink
assignee: user_id of the assignee(OPTIONAL)
epic: epic_id(OPTIONAL)
project_name: project_name
priority_mapping: (OPTIONAL)
HIGH: "High"
MEDIUM: "Medium"
LOW: "Low"
INFO: "Lowest"
scope:
include:
- identifier: [CPUThrottlingHigh, KubePodCrashLooping]
Expand Down
6 changes: 3 additions & 3 deletions helm/robusta/Chart.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ dependencies:
version: 55.7.0
- name: holmes
repository: https://robusta-charts.storage.googleapis.com
version: 0.6.1
digest: sha256:adafc16cc9d9398d9e268b410bbf7461169b9633f6ad8582ad7328f94187824a
generated: "2024-11-08T18:15:36.700936+02:00"
version: 0.7.2
digest: sha256:39c7e9f5e2ec7a59eb3b88627e930294ae2de9bf072b83a792977c1e9a630132
generated: "2024-12-11T13:42:23.882949+02:00"
2 changes: 1 addition & 1 deletion helm/robusta/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ dependencies:
condition: enablePrometheusStack
repository: "https://prometheus-community.github.io/helm-charts"
- name: holmes
version: 0.6.1
version: 0.7.2
condition: enableHolmesGPT
repository: "https://robusta-charts.storage.googleapis.com"
Binary file removed helm/robusta/charts/holmes-0.6.1.tgz
Binary file not shown.
Binary file added helm/robusta/charts/holmes-0.7.2.tgz
Binary file not shown.
5 changes: 3 additions & 2 deletions playbooks/robusta_playbooks/krr.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@
from robusta.integrations.openshift import IS_OPENSHIFT
from robusta.integrations.prometheus.utils import generate_prometheus_config

IMAGE: str = os.getenv("KRR_IMAGE_OVERRIDE", f"{IMAGE_REGISTRY}/krr:v1.17.0")
IMAGE: str = os.getenv("KRR_IMAGE_OVERRIDE", f"{IMAGE_REGISTRY}/krr:v1.18.0")
KRR_MEMORY_LIMIT: str = os.getenv("KRR_MEMORY_LIMIT", "2Gi")
KRR_MEMORY_REQUEST: str = os.getenv("KRR_MEMORY_REQUEST", "2Gi")
KRR_STRATEGY: str = os.getenv("KRR_STRATEGY", "simple")


SeverityType = Literal["CRITICAL", "WARNING", "OK", "GOOD", "UNKNOWN"]
Expand Down Expand Up @@ -119,7 +120,7 @@ class KRRParams(PrometheusParams, PodRunningParams):
"""

serviceAccountName: str = RUNNER_SERVICE_ACCOUNT
strategy: str = "simple"
strategy: str = KRR_STRATEGY
args: Optional[str] = None
krr_args: str = ""
timeout: int = 3600
Expand Down
3 changes: 2 additions & 1 deletion src/robusta/core/sinks/jira/jira_sink_params.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import List, Optional
from typing import Dict, List, Optional

from robusta.core.sinks.sink_base_params import SinkBaseParams
from robusta.core.sinks.sink_config import SinkConfigBase
Expand All @@ -20,6 +20,7 @@ class JiraSinkParams(SinkBaseParams):
noReopenResolution: Optional[str] = ""
epic: Optional[str] = ""
assignee: Optional[str] = ""
priority_mapping: Optional[Dict[str, str]] = None


@classmethod
Expand Down
45 changes: 44 additions & 1 deletion src/robusta/integrations/jira/client.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import logging
from typing import Optional
from typing import Optional, Dict

from requests.auth import HTTPBasicAuth
from requests.exceptions import HTTPError
from requests_toolbelt import MultipartEncoder

from robusta.core.reporting.base import FindingStatus
Expand Down Expand Up @@ -58,6 +59,24 @@ def __init__(self, jira_params: JiraSinkParams):
f"Jira initialized successfully. Project: {self.default_project_id} issue type: {self.default_issue_type_id}"
)

if jira_params.priority_mapping:
if logging.getLogger().getEffectiveLevel() <= logging.DEBUG:
self._validate_priorities(jira_params.priority_mapping)

def _validate_priorities(self, priority_mapping: Dict[str, str]) -> None:
"""Validate that configured priorities exist in Jira"""
endpoint = "priority"
url = self._get_full_jira_url(endpoint)
available_priorities = self._call_jira_api(url) or []
available_priority_names = {p.get("name") for p in available_priorities}

for severity, priority in priority_mapping.items():
if priority not in available_priority_names:
logging.warning(
f"Configured priority '{priority}' for severity '{severity}' "
f"is not available in Jira. Available priorities: {available_priority_names}"
)

def _get_full_jira_url(self, endpoint: str) -> str:
return "/".join([self.params.url, _API_PREFIX, endpoint])

Expand Down Expand Up @@ -160,6 +179,23 @@ def _get_default_project_id(self):
return default_issue["id"]
return None

def _resolve_priority(self, priority_name: str) -> dict:
"""Resolve Jira priority:
1. User configured priority mapping (if defined)
2. Fallback to current behavior (use priority name as-is)
Returns:
dict: Priority field in format {"name": str}
"""
# 1. Try user configured priority mapping
if hasattr(self, "params") and self.params.priority_mapping:
for severity, mapped_name in self.params.priority_mapping.items():
if mapped_name == priority_name:
return {"name": mapped_name}

# 2. Fallback to current behavior
return {"name": priority_name}

def list_issues(self, search_params: Optional[str] = None):
endpoint = "search"
search_params = search_params or ""
Expand Down Expand Up @@ -208,6 +244,13 @@ def comment_issue(self, issue_id, text):
def create_issue(self, issue_data, issue_attachments=None):
endpoint = "issue"
url = self._get_full_jira_url(endpoint)

# Add priority resolution if it exists
if "priority" in issue_data:
priority_name = issue_data["priority"].get("name")
if priority_name:
issue_data["priority"] = self._resolve_priority(priority_name)

payload = {
"update": {},
"fields": {
Expand Down
33 changes: 19 additions & 14 deletions src/robusta/integrations/jira/sender.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
from robusta.core.sinks.jira.jira_sink_params import JiraSinkParams
from robusta.integrations.jira.client import JiraClient

SEVERITY_JIRA_ID = {
FindingSeverity.HIGH: "Critical",
FindingSeverity.MEDIUM: "Major",
FindingSeverity.LOW: "Minor",
FindingSeverity.INFO: "Minor",
}

SEVERITY_EMOJI_MAP = {
FindingSeverity.HIGH: ":red_circle:",
FindingSeverity.MEDIUM: ":large_orange_circle:",
Expand All @@ -30,13 +37,6 @@
FindingSeverity.LOW: "#ffdc06",
FindingSeverity.INFO: "#05aa01",
}
# Jira priorities, see: https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-priorities/#api-group-issue-priorities
SEVERITY_JIRA_ID = {
FindingSeverity.HIGH: "Critical",
FindingSeverity.MEDIUM: "Major",
FindingSeverity.LOW: "Minor",
FindingSeverity.INFO: "Minor",
}

STRONG_MARK_REGEX = r"\*{1}[\w|\s\d%!><=\-:;@#$%^&()\.\,\]\[\\\/'\"]+\*{1}"
ITALIAN_MARK_REGEX = r"(^|\s+)_{1}[\w|\s\d%!*><=\-:;@#$%^&()\.\,\]\[\\\/'\"]+_{1}(\s+|$)"
Expand Down Expand Up @@ -237,16 +237,21 @@ def send_finding_to_jira(
FindingStatus.RESOLVED if finding.title.startswith("[RESOLVED]") else FindingStatus.FIRING
)

# Default priority is "Major" if not a standard severity is given
# Use user priority mapping if available, otherwise fall back to default
severity = SEVERITY_JIRA_ID.get(finding.severity, "Major")
if self.params.priority_mapping:
severity = self.params.priority_mapping.get(finding.severity.name, severity)

issue_data = {
"description": {"type": "doc", "version": 1, "content": actions + output_blocks},
"summary": finding.title,
"labels": labels,
"priority": {"name": severity},
}

# Let client.manage_issue handle the fallback to ID if name fails
self.client.manage_issue(
{
"description": {"type": "doc", "version": 1, "content": actions + output_blocks},
"summary": finding.title,
"labels": labels,
"priority": {"name": severity},
},
issue_data,
{"status": status, "source": finding.source},
file_blocks,
)

0 comments on commit 8f2e664

Please sign in to comment.