From c34034a2476b4e726faeece5fcceb77f7a33375e Mon Sep 17 00:00:00 2001 From: Ganesh Rathinavel Medayil Date: Thu, 14 Mar 2024 12:56:02 +0530 Subject: [PATCH] New improved guess cached resource owner name --- playbooks/robusta_playbooks/babysitter.py | 1 + .../robusta_playbooks/event_enrichments.py | 2 + scripts/generate_kubernetes_code.py | 4 ++ .../core/discovery/top_service_resolver.py | 62 ++++++++++++++++--- .../playbooks/internal/discovery_events.py | 7 ++- src/robusta/core/reporting/base.py | 7 ++- src/robusta/core/reporting/consts.py | 3 + .../core/reporting/finding_subjects.py | 3 + src/robusta/core/reporting/findings.py | 9 +++ .../core/triggers/error_event_trigger.py | 6 +- .../kubernetes/autogenerated/events.py | 40 ++++++++++++ src/robusta/integrations/prometheus/models.py | 18 +++++- 12 files changed, 149 insertions(+), 13 deletions(-) create mode 100644 src/robusta/core/reporting/findings.py diff --git a/playbooks/robusta_playbooks/babysitter.py b/playbooks/robusta_playbooks/babysitter.py index 1cbb66427..e7fbfae3d 100644 --- a/playbooks/robusta_playbooks/babysitter.py +++ b/playbooks/robusta_playbooks/babysitter.py @@ -23,6 +23,7 @@ FindingAggregationKey, ) from robusta.core.reporting.base import EnrichmentType +from robusta.core.reporting.findings import FindingOwner class BabysitterConfig(ActionParams): diff --git a/playbooks/robusta_playbooks/event_enrichments.py b/playbooks/robusta_playbooks/event_enrichments.py index dae643934..1c9e05c28 100644 --- a/playbooks/robusta_playbooks/event_enrichments.py +++ b/playbooks/robusta_playbooks/event_enrichments.py @@ -40,6 +40,7 @@ ) from robusta.core.reporting import EventsBlock, EventRow from robusta.core.reporting.base import EnrichmentType +from robusta.core.reporting.findings import FindingOwner from robusta.core.reporting.custom_rendering import render_value @@ -72,6 +73,7 @@ def event_report(event: EventChangeEvent): subject_type=FindingSubjectType.from_kind(k8s_obj.kind), namespace=k8s_obj.namespace, node=KubeObjFindingSubject.get_node_name(k8s_obj), + owner=FindingOwner(owner_references=event.obj.metadata.ownerReferences) ), ) event.add_finding(finding) diff --git a/scripts/generate_kubernetes_code.py b/scripts/generate_kubernetes_code.py index bca5f6e87..fe4eb946f 100755 --- a/scripts/generate_kubernetes_code.py +++ b/scripts/generate_kubernetes_code.py @@ -66,6 +66,7 @@ def autogenerate_events(f: TextIO): from ..base_event import K8sBaseChangeEvent from ....core.model.events import ExecutionBaseEvent, ExecutionEventBaseParams from ....core.reporting.base import FindingSubject + from ....core.reporting.findings import FindingOwner from ....core.reporting.consts import FindingSubjectType, FindingSource from ....core.reporting.finding_subjects import KubeObjFindingSubject from robusta.integrations.kubernetes.custom_models import {CUSTOM_SUBCLASSES_NAMES_STR} @@ -169,6 +170,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @classmethod @@ -249,6 +251,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -269,6 +272,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) diff --git a/src/robusta/core/discovery/top_service_resolver.py b/src/robusta/core/discovery/top_service_resolver.py index f073958f4..dd17fcb92 100644 --- a/src/robusta/core/discovery/top_service_resolver.py +++ b/src/robusta/core/discovery/top_service_resolver.py @@ -3,9 +3,12 @@ from collections import defaultdict from typing import Dict, List, Optional +from hikaru.model.rel_1_26 import OwnerReference from pydantic.main import BaseModel from robusta.core.model.env_vars import RESOURCE_UPDATES_CACHE_TTL_SEC +from robusta.core.reporting.findings import FindingOwner +from robusta.integrations.kubernetes.custom_models import RobustaPod class TopLevelResource(BaseModel): @@ -26,6 +29,7 @@ class TopServiceResolver: __recent_resource_updates: Dict[str, CachedResourceInfo] = {} __namespace_to_resource: Dict[str, List[TopLevelResource]] = defaultdict(list) __cached_updates_lock = threading.Lock() + __cached_owner_references: Dict[str, OwnerReference] = {} @classmethod def store_cached_resources(cls, resources: List[TopLevelResource]): @@ -51,20 +55,62 @@ def store_cached_resources(cls, resources: List[TopLevelResource]): # TODO remove this guess function # temporary try to guess who the owner service is. @classmethod - def guess_service_key(cls, name: str, namespace: str) -> str: - resource = cls.guess_cached_resource(name, namespace) + def guess_service_key(cls, name: str, namespace: str, kind: str, owner: Optional[FindingOwner]) -> str: + resource = cls.guess_cached_resource(name, namespace, kind=kind, owner=owner) return resource.get_resource_key() if resource else "" - # TODO remove this guess function - # temporary try to guess who the owner service is. @classmethod - def guess_cached_resource(cls, name: str, namespace: str) -> Optional[TopLevelResource]: + def get_pod_owner_reference(cls, name: str, namespace: str) -> Optional[OwnerReference]: + key = f"{namespace}/{name}" + if key in cls.__cached_owner_references: + return cls.__cached_owner_references[key] + + robusta_pod = RobustaPod.find_pod(name, namespace) + if robusta_pod.metadata.ownerReferences: + cls.__cached_owner_references[key] = robusta_pod.metadata.ownerReferences[0] + return robusta_pod.metadata.ownerReferences[0] + + return None + + @classmethod + def guess_cached_resource(cls, name: str, namespace: str, kind: str, owner: Optional[FindingOwner]) \ + -> Optional[TopLevelResource]: if name is None or namespace is None: return None - for cached_resource in cls.__namespace_to_resource[namespace]: - if name.startswith(cached_resource.name): - return cached_resource + kind = kind.lower() + + # owner references available + if owner and owner.owner_references: + owner_kind = owner.owner_references[0].kind.lower() + owner_reference = owner.owner_references[0] + + if owner_kind in ["deployment", "statefulset", "daemonset", "job", "deploymentconfig", + "argorollout"]: + return TopLevelResource(name=owner_reference.name, resource_type=owner_reference.kind, + namespace=namespace) + + # replicset + if owner_kind == "replicaset": + new_owner_reference = cls.get_pod_owner_reference(name=owner_reference.name, namespace=namespace) + return TopLevelResource(name=new_owner_reference.name, resource_type=new_owner_reference.kind, + namespace=namespace) + + # crd + if owner_kind not in ["deployment", "statefulset", "daemonset", "job", "deploymentconfig", + "argorollout", "pod"]: + return TopLevelResource(name=name, resource_type=kind, namespace=namespace) + + # owner references NOT available + if owner is None or not owner.owner_references: + return TopLevelResource(name=name, resource_type=kind, namespace=namespace) + + # unknown owner + if owner.unknown_owner: + for cached_resource in cls.__namespace_to_resource[namespace]: + if name.startswith(cached_resource.name): + return cached_resource + return None @classmethod diff --git a/src/robusta/core/playbooks/internal/discovery_events.py b/src/robusta/core/playbooks/internal/discovery_events.py index 6fcf1cc30..81bb80395 100644 --- a/src/robusta/core/playbooks/internal/discovery_events.py +++ b/src/robusta/core/playbooks/internal/discovery_events.py @@ -16,6 +16,7 @@ from robusta.core.discovery.top_service_resolver import TopLevelResource, TopServiceResolver from robusta.core.playbooks.common import get_event_timestamp, get_events_list from robusta.core.reporting.base import EnrichmentType +from robusta.core.reporting.findings import FindingOwner @action @@ -65,6 +66,7 @@ def create_debug_event_finding(event: Event): """ k8s_obj = event.regarding subject_type = FindingSubjectType.from_kind(k8s_obj.kind.lower()) if k8s_obj.kind else FindingSubjectType.TYPE_NONE + finding = Finding( title=f"{event.reason} {event.type} for {k8s_obj.kind} {k8s_obj.namespace}/{k8s_obj.name}", description=event.note, @@ -76,10 +78,13 @@ def create_debug_event_finding(event: Event): k8s_obj.name, subject_type, k8s_obj.namespace, + owner=FindingOwner(owner_references=event.metadata.ownerReferences) ), creation_date=get_event_timestamp(event), ) - finding.service_key = TopServiceResolver.guess_service_key(name=k8s_obj.name, namespace=k8s_obj.namespace) + finding.service_key = TopServiceResolver.guess_service_key(name=k8s_obj.name, namespace=k8s_obj.namespace, + kind=k8s_obj.kind, + owner=finding.subject.owner) return finding diff --git a/src/robusta/core/reporting/base.py b/src/robusta/core/reporting/base.py index 48ce393b0..27d1c670e 100644 --- a/src/robusta/core/reporting/base.py +++ b/src/robusta/core/reporting/base.py @@ -13,6 +13,7 @@ from robusta.core.discovery.top_service_resolver import TopServiceResolver from robusta.core.model.env_vars import ROBUSTA_UI_DOMAIN from robusta.core.reporting.consts import FindingSource, FindingSubjectType, FindingType +from robusta.core.reporting.findings import FindingOwner class BaseBlock(BaseModel): @@ -231,6 +232,7 @@ def __init__( container: Optional[str] = None, labels: Optional[Dict[str, str]] = None, annotations: Optional[Dict[str, str]] = None, + owner: Optional[FindingOwner] = None, ): self.name = name self.subject_type = subject_type @@ -239,6 +241,7 @@ def __init__( self.container = container self.labels = labels or {} self.annotations = annotations or {} + self.owner = owner def __str__(self): if self.namespace is not None: @@ -281,7 +284,9 @@ def __init__( self.subject = subject self.enrichments: List[Enrichment] = [] self.video_links: List[VideoLink] = [] - self.service = TopServiceResolver.guess_cached_resource(name=subject.name, namespace=subject.namespace) + self.service = TopServiceResolver.guess_cached_resource(name=subject.name, namespace=subject.namespace, + kind=subject.subject_type.value, + owner=subject.owner) self.service_key = self.service.get_resource_key() if self.service else "" uri_path = f"services/{self.service_key}?tab=grouped" if self.service_key else "graphs" self.investigate_uri = f"{ROBUSTA_UI_DOMAIN}/{uri_path}" diff --git a/src/robusta/core/reporting/consts.py b/src/robusta/core/reporting/consts.py index 11e4a6b85..c234bbd42 100644 --- a/src/robusta/core/reporting/consts.py +++ b/src/robusta/core/reporting/consts.py @@ -47,6 +47,7 @@ class FindingSubjectType(Enum): TYPE_DAEMONSET = "daemonset" TYPE_STATEFULSET = "statefulset" TYPE_HPA = "horizontalpodautoscaler" + TYPE_REPLICASET = "replicaset" TYPE_HELM_RELEASES = "helmreleases" @staticmethod @@ -62,6 +63,8 @@ def from_kind(kind: str): return FindingSubjectType.TYPE_JOB elif kind == "daemonset": return FindingSubjectType.TYPE_DAEMONSET + elif kind == "replicaset": + return FindingSubjectType.TYPE_REPLICASET elif kind == "statefulset": return FindingSubjectType.TYPE_STATEFULSET elif kind == "horizontalpodautoscaler": diff --git a/src/robusta/core/reporting/finding_subjects.py b/src/robusta/core/reporting/finding_subjects.py index d7672f542..793a380c0 100644 --- a/src/robusta/core/reporting/finding_subjects.py +++ b/src/robusta/core/reporting/finding_subjects.py @@ -1,6 +1,7 @@ from hikaru.model.rel_1_26 import ObjectReference, Pod from robusta.core.reporting.base import FindingSubject +from robusta.core.reporting.findings import FindingOwner from robusta.core.reporting.consts import FindingSubjectType @@ -23,6 +24,7 @@ def __init__( node=node_name, labels=obj.metadata.labels, annotations=obj.metadata.annotations, + owner=FindingOwner(owner_references=obj.metadata.ownerReferences) ) @staticmethod @@ -49,4 +51,5 @@ def __init__(self, pod: Pod = None): node=pod.spec.nodeName, labels=pod.metadata.labels, annotations=pod.metadata.annotations, + owner=FindingOwner(owner_references=pod.metadata.ownerReferences) ) diff --git a/src/robusta/core/reporting/findings.py b/src/robusta/core/reporting/findings.py new file mode 100644 index 000000000..f94786747 --- /dev/null +++ b/src/robusta/core/reporting/findings.py @@ -0,0 +1,9 @@ +from typing import Optional, List + +from hikaru.model.rel_1_26 import OwnerReference +from pydantic import BaseModel + + +class FindingOwner(BaseModel): + owner_references: Optional[List[OwnerReference]] = None + unknown_owner: bool = False diff --git a/src/robusta/core/triggers/error_event_trigger.py b/src/robusta/core/triggers/error_event_trigger.py index 095251dce..f2f2587ab 100644 --- a/src/robusta/core/triggers/error_event_trigger.py +++ b/src/robusta/core/triggers/error_event_trigger.py @@ -2,6 +2,7 @@ from robusta.core.discovery.top_service_resolver import TopServiceResolver from robusta.core.playbooks.base_trigger import TriggerEvent +from robusta.core.reporting.findings import FindingOwner from robusta.integrations.kubernetes.autogenerated.triggers import EventAllChangesTrigger, EventChangeEvent from robusta.integrations.kubernetes.base_triggers import K8sTriggerEvent from robusta.utils.rate_limiter import RateLimiter @@ -70,7 +71,10 @@ def should_fire(self, event: TriggerEvent, playbook_id: str, build_context: Dict # Perform a rate limit for this service key according to the rate_limit parameter name = exec_event.obj.regarding.name if exec_event.obj.regarding.name else "" namespace = exec_event.obj.regarding.namespace if exec_event.obj.regarding.namespace else "" - service_key = TopServiceResolver.guess_service_key(name=name, namespace=namespace) + kind = exec_event.obj.regarding.kind if exec_event.obj.regarding.kind else "" + service_key = (TopServiceResolver.guess_service_key + (name=name, namespace=namespace, kind=kind, + owner=FindingOwner(owner_references=exec_event.obj.metadata.ownerReferences))) return RateLimiter.mark_and_test( f"WarningEventTrigger_{playbook_id}_{exec_event.obj.reason}", service_key if service_key else namespace + ":" + name, diff --git a/src/robusta/integrations/kubernetes/autogenerated/events.py b/src/robusta/integrations/kubernetes/autogenerated/events.py index 89006f162..0b71c79d0 100644 --- a/src/robusta/integrations/kubernetes/autogenerated/events.py +++ b/src/robusta/integrations/kubernetes/autogenerated/events.py @@ -59,6 +59,7 @@ from ....core.model.events import ExecutionBaseEvent, ExecutionEventBaseParams from ....core.reporting.base import FindingSubject +from ....core.reporting.findings import FindingOwner from ....core.reporting.consts import FindingSource, FindingSubjectType from ....core.reporting.finding_subjects import KubeObjFindingSubject from ..base_event import K8sBaseChangeEvent @@ -194,6 +195,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @classmethod @@ -317,6 +319,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -336,6 +339,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -369,6 +373,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -388,6 +393,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -421,6 +427,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -440,6 +447,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -473,6 +481,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -492,6 +501,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -525,6 +535,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -544,6 +555,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -577,6 +589,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -596,6 +609,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -629,6 +643,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -648,6 +663,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -683,6 +699,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -702,6 +719,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -734,6 +752,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -753,6 +772,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -785,6 +805,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -804,6 +825,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -836,6 +858,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -855,6 +878,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -888,6 +912,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -907,6 +932,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -939,6 +965,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -958,6 +985,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -991,6 +1019,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -1010,6 +1039,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -1042,6 +1072,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -1061,6 +1092,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -1096,6 +1128,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -1115,6 +1148,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -1148,6 +1182,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -1167,6 +1202,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -1200,6 +1236,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -1219,6 +1256,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -1252,6 +1290,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) @@ -1271,6 +1310,7 @@ def get_subject(self) -> FindingSubject: node=KubeObjFindingSubject.get_node_name(self.obj), labels=self.obj.metadata.labels, annotations=self.obj.metadata.annotations, + owner=FindingOwner(owner_references=self.obj.metadata.ownerReferences) ) diff --git a/src/robusta/integrations/prometheus/models.py b/src/robusta/integrations/prometheus/models.py index 2e32798e1..7d8152ff4 100644 --- a/src/robusta/integrations/prometheus/models.py +++ b/src/robusta/integrations/prometheus/models.py @@ -6,10 +6,11 @@ from typing import Any, Dict, List, Optional, Union from urllib.parse import parse_qs, unquote, urlparse -from hikaru.model.rel_1_26 import DaemonSet, HorizontalPodAutoscaler, Node, StatefulSet +from hikaru.model.rel_1_26 import DaemonSet, HorizontalPodAutoscaler, Node, StatefulSet, OwnerReference from pydantic import BaseModel from robusta.core.reporting import Finding, FindingSeverity, FindingSource, FindingSubject, FindingSubjectType +from robusta.core.reporting.findings import FindingOwner from robusta.integrations.kubernetes.autogenerated.events import ( DaemonSetEvent, DeploymentEvent, @@ -139,30 +140,36 @@ def get_alert_subject(self) -> FindingSubject: container: Optional[str] = self.alert.labels.get("container") labels = {} annotations = {} + owner_references: Optional[List[OwnerReference]] = None + unknown_owner = False if self.deployment: subject_type = FindingSubjectType.TYPE_DEPLOYMENT name = self.deployment.metadata.name namespace = self.deployment.metadata.namespace labels = self.deployment.metadata.labels annotations = self.deployment.metadata.annotations + owner_references = self.deployment.metadata.ownerReferences elif self.daemonset: subject_type = FindingSubjectType.TYPE_DAEMONSET name = self.daemonset.metadata.name namespace = self.daemonset.metadata.namespace labels = self.daemonset.metadata.labels annotations = self.daemonset.metadata.annotations + owner_references = self.daemonset.metadata.ownerReferences elif self.statefulset: subject_type = FindingSubjectType.TYPE_STATEFULSET name = self.statefulset.metadata.name namespace = self.statefulset.metadata.namespace labels = self.statefulset.metadata.labels annotations = self.statefulset.metadata.annotations + owner_references = self.statefulset.metadata.ownerReferences elif self.node: subject_type = FindingSubjectType.TYPE_NODE name = self.node.metadata.name node_name = self.node.metadata.name labels = self.node.metadata.labels annotations = self.node.metadata.annotations + owner_references = self.node.metadata.ownerReferences elif self.pod: subject_type = FindingSubjectType.TYPE_POD name = self.pod.metadata.name @@ -170,23 +177,30 @@ def get_alert_subject(self) -> FindingSubject: node_name = self.pod.spec.nodeName labels = self.pod.metadata.labels annotations = self.pod.metadata.annotations + owner_references = self.pod.metadata.ownerReferences elif self.job: subject_type = FindingSubjectType.TYPE_JOB name = self.job.metadata.name namespace = self.job.metadata.namespace labels = self.job.metadata.labels annotations = self.job.metadata.annotations + owner_references = self.job.metadata.ownerReferences elif self.hpa: subject_type = FindingSubjectType.TYPE_HPA name = self.hpa.metadata.name labels = self.hpa.metadata.labels annotations = self.hpa.metadata.annotations + owner_references = self.hpa.metadata.ownerReferences + + else: + unknown_owner = True # Add alert labels and annotations. On duplicates, alert labels/annotations are taken labels = {**labels, **self.alert.labels} annotations = {**annotations, **self.alert.annotations} - return FindingSubject(name, subject_type, namespace, node_name, container, labels, annotations) + return FindingSubject(name, subject_type, namespace, node_name, container, labels, annotations, + owner=FindingOwner(owner_references=owner_references, unknown_owner=unknown_owner)) def create_default_finding(self) -> Finding: alert_subject = self.get_alert_subject()