From cf05965352defdf15017ed9ecfcef3383d4abb6a Mon Sep 17 00:00:00 2001 From: Ivan Kovnatsky <75213+ivankovnatsky@users.noreply.github.com> Date: Tue, 10 Dec 2024 16:57:33 +0200 Subject: [PATCH 1/3] Add option to customize Jira priorities (#1637) * Add option to parametries custom Jira priorities * Build and push image to my own ghcr repo * Update Jira documentation * Revert "Build and push image to my own ghcr repo" This reverts commit 2765a877dc51932294e4b4f2b93c17503680b5c5. * Put back changed before autofmt * Fallback to using priority id when priority is not found * Revert "Revert "Build and push image to my own ghcr repo"" This reverts commit 6c8d2b1904f60facb337b95b7b5f31eee495642a. * Correct fallback mechanism for priority IDs * Add constants for jira integration * Correct FindingSeverity import * Correct HTTP method parameter in Jira API calls Fix TypeError in process_request() where method parameter was being passed twice - once as positional argument and once in kwargs. Standardize to use positional argument only. Error: "process_request() got multiple values for argument 'method'" * Use HttpMethod enum instead of string in Jira API calls * Correct jira add attachments method * Fix json structure for payload * Return back url and endpoint for def create_issue * Fix priority handling cascade with fallback logic * Remove specific if condition for HttpMethod code and text * Use more generate approach Exception * Make sure _call_jira_api returns HTTPError * Revert back auto fmt apply * Correct indentations for create_issue comment * Create _resolve_priority method * Put back else clause in manage_issue method * Add a test call to validate common priority * Correct _resolve_priority method comment * Revert "Revert "Revert "Build and push image to my own ghcr repo""" This reverts commit 31f3d590dc9b365e48ab0aa76328dbafccd6c118. * Simplify Jira priority name mapping * Only validate priorities when logging level is DEBUG * Fallback to creating Jira issues without priority if setting priority fails When creating a Jira issue, if setting the priority fails, retry without setting the priority. * Revert to handle only two cases: used defined and default priority names * Remove _create_issue_payload and _handle_attachment_and_return methods * Revert to return None when Jira API error occurs --- docs/configuration/sinks/jira.rst | 14 ++++++ .../core/sinks/jira/jira_sink_params.py | 3 +- src/robusta/integrations/jira/client.py | 45 ++++++++++++++++++- src/robusta/integrations/jira/sender.py | 33 ++++++++------ 4 files changed, 79 insertions(+), 16 deletions(-) diff --git a/docs/configuration/sinks/jira.rst b/docs/configuration/sinks/jira.rst index e76015b04..d8bd53db2 100644 --- a/docs/configuration/sinks/jira.rst +++ b/docs/configuration/sinks/jira.rst @@ -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 `__ to get your project id. * ``issue_type_id_override`` : [Optional - default: None] If available, will override the ``issue_type`` configuration. Follow these `instructions `__ to get your issue id. @@ -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] diff --git a/src/robusta/core/sinks/jira/jira_sink_params.py b/src/robusta/core/sinks/jira/jira_sink_params.py index 3e5a9561c..9824128de 100644 --- a/src/robusta/core/sinks/jira/jira_sink_params.py +++ b/src/robusta/core/sinks/jira/jira_sink_params.py @@ -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 @@ -20,6 +20,7 @@ class JiraSinkParams(SinkBaseParams): noReopenResolution: Optional[str] = "" epic: Optional[str] = "" assignee: Optional[str] = "" + priority_mapping: Optional[Dict[str, str]] = None @classmethod diff --git a/src/robusta/integrations/jira/client.py b/src/robusta/integrations/jira/client.py index e53c197fb..0e8e42a90 100644 --- a/src/robusta/integrations/jira/client.py +++ b/src/robusta/integrations/jira/client.py @@ -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 @@ -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]) @@ -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 "" @@ -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": { diff --git a/src/robusta/integrations/jira/sender.py b/src/robusta/integrations/jira/sender.py index 42d26a808..4cfcb5a3d 100644 --- a/src/robusta/integrations/jira/sender.py +++ b/src/robusta/integrations/jira/sender.py @@ -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:", @@ -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+|$)" @@ -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, ) From 5241c7ab242d42ea063df29045bc23c25f8c6f20 Mon Sep 17 00:00:00 2001 From: Roi Glinik Date: Wed, 11 Dec 2024 10:47:10 +0200 Subject: [PATCH 2/3] holmes and krr update (#1658) --- helm/robusta/Chart.yaml | 2 +- playbooks/robusta_playbooks/krr.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/helm/robusta/Chart.yaml b/helm/robusta/Chart.yaml index 3231afaf4..55a0c1bd1 100644 --- a/helm/robusta/Chart.yaml +++ b/helm/robusta/Chart.yaml @@ -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" diff --git a/playbooks/robusta_playbooks/krr.py b/playbooks/robusta_playbooks/krr.py index dfbedd780..d13e24998 100644 --- a/playbooks/robusta_playbooks/krr.py +++ b/playbooks/robusta_playbooks/krr.py @@ -33,7 +33,7 @@ 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") From a75766eae78de68436835dc1270775727b5143c8 Mon Sep 17 00:00:00 2001 From: Roi Glinik Date: Wed, 11 Dec 2024 16:15:33 +0200 Subject: [PATCH 3/3] add option to use different strategy , upgrade holmes (#1660) --- helm/robusta/Chart.lock | 6 +++--- helm/robusta/charts/holmes-0.6.1.tgz | Bin 2471 -> 0 bytes helm/robusta/charts/holmes-0.7.2.tgz | Bin 0 -> 2530 bytes playbooks/robusta_playbooks/krr.py | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) delete mode 100644 helm/robusta/charts/holmes-0.6.1.tgz create mode 100644 helm/robusta/charts/holmes-0.7.2.tgz diff --git a/helm/robusta/Chart.lock b/helm/robusta/Chart.lock index 744b0e0b7..d1588b552 100644 --- a/helm/robusta/Chart.lock +++ b/helm/robusta/Chart.lock @@ -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" diff --git a/helm/robusta/charts/holmes-0.6.1.tgz b/helm/robusta/charts/holmes-0.6.1.tgz deleted file mode 100644 index 4dd137eab14a44e5da7974c2639acbd15b3f60da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2471 zcmV;Y30U?YiwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PI?AbK5rZ&ue~){mXCKOi59GN=NVVcihx-POPy!zPagiJP^2| z5Q6{;fN~Yb_t|g22k}9aCA&^?Hx*vU=5n#S0Cs=a1vryDLDN5(5p9D7N%*UmZ($gQ z$A^d6-!Kf@f5ZJSd^I?HGdMUr7z~aNUWJ4G!=r;&5Pk()R$6K-(XYb4tgAh^|B*%% zyGCu86hoNrdqk<_V;CF<11~~DHB&Yt{hE2p`_UEriaY`8yauLH!#~mqYJnDwCrE-r z$oaRBHT*37b9lyj(;H9m5CPBdl%wK_%5 zquJ9K^_pg(Zr2rs8 zNL%*2e{iD8RdI4O8kuT}#);7QM~cQ~{{v8!4q*_6N%oXrBHe-v!u`V!%md&oVI^zx z<@fgPUE9*u4OL!S0iszcvmOQC&@AVC_w#VqDoyY67hM z7fJYfAzwf+5uvbf+|=AKg-T%w6R4N6eQ1;sED$SoD5h@}rnn9#mO;e)0b1 z{Pgj=4YN>3k@TwhMS<)JVe!V#Va5}yjU5u}8Mwb^Sr{nS2$M^2v z52LFMCUzxUTiMzOj+KF5lv}=jh&8q^@JA}M$aOo;BU?kQGaFZ+}d)KN=qxLnC}d#kiX1QRLT=2wxHXAKn% z=Z(juD^w)q{Lfevw#XgUzM-73#B>kdJ@kzwHf_UM_s)x z;B$pgz-euuB2to-l+7zp_kyP{$WzvOD@{bPq6V)={#yM+vBu17vSO{?wV<=!7xVCT z*kZbf6DA@i;^)lH+Q~_+G?Z{A;q4xG^lnT3VwiVNcQ%ei z=R9Y-Sj3D@vs!YJMsew<`38qH)yyuQI2E0Cti|GP!&ZQJJq-I$=XwT8yXQR^X_=s% zVQS#y@|{^XjzkJ(rFKq5rABE2DwDN48AojsB1l}{_8ZjxlnKJw=k*M&#H0Q)Pn~K> z$#M+XP3+Rvc;C))mhVX1z`*v4#|0NWE#Y=XgfuQU*WJV}p^f(~fre_Lustb7E>SF8 zBZFI|X*ubfx~zhD4hn8MmMJQzr`15K_cz;}*0jo>=B-gsyZz7E>z@M@z3-TvZQZ|l z0aQEWJ6tW5Y7dWF*>wxGdjHgM^R!7$W!`>aL}5>R2_LOxSIXf3&%x;VMXq1KndcrZ zaq0w=!1RbQ9>8iz{?z)~q?1ob^HVw>Ivp3_Sq{6M%T}xKy-z{@zHR0FuQ-@4&ZS@E zJCIG^{~o+)fB$!UaJ>8e@0+v&j$;cya1^!coBVp>N679fJNLMg@|vRq{T{pJ*ajL( zX}O%6LQdw5L-^un5VQPzt=NoKub_(C@Y>eYgq_`$LK*+T40*z0AvM0dj!p4@ba2%E z{`dIJ?)(35()M6PtVJyhSP8iYz}*Z5Oj5>OW&(=PTN0xQygj&@F#{%5+3g;f8FCIW zmy`U9EGFWgKqDuX%@Gu_vl^X<$lC*fahAux4@%>d{fSYYb^hOv0bB^a04cJb&LmK% zfir=D7rYzajIGq@?ZJsm5-H&NWDF70#tUL*``K@9z86eB>wflI5@vDV{gjVpF8Y*c>GCHpU=fmj l&aUMrFPIxDBkb?sW!H9X*Z!N^e*ypi|Nj%h3N-*Y002!4*>?Z{ diff --git a/helm/robusta/charts/holmes-0.7.2.tgz b/helm/robusta/charts/holmes-0.7.2.tgz new file mode 100644 index 0000000000000000000000000000000000000000..c60469129a2569268da27412e219c99f78f296bf GIT binary patch literal 2530 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