Skip to content

Commit

Permalink
Namespaced logs and cluster events (#121)
Browse files Browse the repository at this point in the history
* namespaced events collection + litmus deprecation

Signed-off-by: Tullio Sebastiani <[email protected]>

* namespaced logs collection

Signed-off-by: Tullio Sebastiani <[email protected]>

linting

Signed-off-by: Tullio Sebastiani <[email protected]>

typos

Signed-off-by: Tullio Sebastiani <[email protected]>

typo

Signed-off-by: Tullio Sebastiani <[email protected]>

namespaced events collection test fix

Signed-off-by: Tullio Sebastiani <[email protected]>

constructor

Signed-off-by: Tullio Sebastiani <[email protected]>

---------

Signed-off-by: Tullio Sebastiani <[email protected]>
  • Loading branch information
tsebastiani authored Sep 12, 2024
1 parent 741192e commit aaa674a
Show file tree
Hide file tree
Showing 17 changed files with 1,491 additions and 780 deletions.
1,499 changes: 769 additions & 730 deletions poetry.lock

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "krkn-lib"
version = "0.0.0"
version = "1.0.0"
description = "Foundation library for Kraken"
authors = ["Red Hat Chaos Team"]
license = "Apache-2.0"
Expand All @@ -25,6 +25,8 @@ elasticsearch = "7.13.4"
elasticsearch-dsl = "7.4.1"
wheel = "^0.42.0"
cython = "3.0"
numpy= "1.26.4"
deprecation="2.1.0"

[tool.poetry.group.test.dependencies]
jinja2 = "^3.1.2"
Expand Down
163 changes: 162 additions & 1 deletion src/krkn_lib/k8s/krkn_kubernetes.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
from functools import partial
from queue import Queue
from typing import Any, Dict, List, Optional
from krkn_lib.version import __version__

import arcaflow_lib_kubernetes
import deprecation
import kubernetes
import urllib3
import yaml
Expand All @@ -40,7 +42,7 @@
Volume,
VolumeMount,
)
from krkn_lib.models.telemetry import NodeInfo, Taint
from krkn_lib.models.telemetry import NodeInfo, Taint, ClusterEvent
from krkn_lib.utils import filter_dictionary, get_random_string
from krkn_lib.utils.safe_logger import SafeLogger

Expand Down Expand Up @@ -199,6 +201,44 @@ def __initialize_clients_from_kconfig_string(self, kubeconfig_str: str):
logging.error("failed to validate kubeconfig: %s\n", str(e))
raise e

def _get_clusterversion_string(self) -> str:
"""
Return clusterversion status text on OpenShift, empty string
on other distributions
*** IT MUST BE CONSIDERED A PRIVATE METHOD (CANNOT USE
*** DOUBLE UNDERSCORE TO MANTAIN IT VISIBLE ON SUB-CLASSES)
*** USED IN KrknKubernetes and KrknOpenshift TO AUTODETECT
*** THE CLUSTER TYPE
:return: clusterversion status
"""

try:
cvs = self.custom_object_client.list_cluster_custom_object(
"config.openshift.io",
"v1",
"clusterversions",
)
for cv in cvs["items"]:
for condition in cv["status"]["conditions"]:
if condition["type"] == "Available":
return condition["message"].split(" ")[-1]
return ""
except client.exceptions.ApiException as e:
if e.status == 404:
return ""
else:
raise e

def is_kubernetes(self) -> bool:
"""
Checks if it's a kubernetes cluster
:return: true if it's kubernetes false if not
"""
value = self._get_clusterversion_string()
return value is None or value == ""

def get_kubeconfig_path(self) -> str:
"""
Returns a path of the kubeconfig with which
Expand Down Expand Up @@ -322,6 +362,19 @@ def list_namespaces(self, label_selector: str = None) -> list[str]:
namespaces.append(namespace.metadata.name)
return namespaces

def list_namespaces_by_regex(self, regex: str) -> list[str]:
"""
Lists all the namespaces matching a regex
:param regex: The regular expression against which the
namespaces will be compared
:return: a list of namespaces matching the regex
"""
namespaces = self.list_namespaces()
filtered_namespaces = [ns for ns in namespaces if re.match(regex, ns)]
return filtered_namespaces

def get_namespace_status(self, namespace_name: str) -> str:
"""
Get status of a given namespace
Expand Down Expand Up @@ -1522,6 +1575,12 @@ def get_pod_info(self, name: str, namespace: str = "default") -> Pod:
)
return None

@deprecation.deprecated(
deprecated_in="3.1.0",
removed_in="3.2.0",
current_version=__version__,
details="litmus support dropped from krkn",
)
def get_litmus_chaos_object(
self, kind: str, name: str, namespace: str = "default"
) -> LitmusChaosObject:
Expand Down Expand Up @@ -2467,13 +2526,90 @@ def is_pod_terminating(self, pod_name: str, namespace: str) -> bool:
except Exception:
return False

def collect_and_parse_cluster_events(
self,
start_timestamp: int,
end_timestamp: int,
local_timezone: str,
cluster_timezone: str = "UTC",
limit: int = 500,
namespace: str = None,
) -> list[ClusterEvent]:
"""
Collects cluster events querying `/api/v1/events`
filtered in a given time interval and writes them in
a temporary file in json format.
:param start_timestamp: timestamp of the minimum date
after that the event is relevant
:param end_timestamp: timestamp of the maximum date
before that the event is relevant
:param local_timezone: timezone of the local system
:param cluster_timezone: timezone of the remote cluster
:param limit: limit of the number of events to be fetched
from the cluster
:param namespace: Namespace from which the events must be
collected, if None all-namespaces will be selected
:return: Returns a list of parsed ClusterEvents
"""
events = []
try:
path_params: Dict[str, str] = {}
query_params = {"limit": limit}
header_params: Dict[str, str] = {}
auth_settings = ["BearerToken"]
header_params["Accept"] = self.api_client.select_header_accept(
["application/json"]
)

path = "/api/v1/events"
if namespace:
path = f"/api/v1/namespaces/{namespace}/events"

(data) = self.api_client.call_api(
path,
"GET",
path_params,
query_params,
header_params,
response_type="str",
auth_settings=auth_settings,
)

json_obj = ast.literal_eval(data[0])
events_list = reversed(json_obj["items"])
for obj in events_list:
filtered_obj = filter_dictionary(
obj,
"firstTimestamp",
start_timestamp,
end_timestamp,
cluster_timezone,
local_timezone,
)
if filtered_obj:
events.append(ClusterEvent(k8s_json_dict=obj))

except Exception as e:
logging.error(str(e))

return events

@deprecation.deprecated(
deprecated_in="3.1.0",
removed_in="3.2.0",
current_version=__version__,
details="replaced by `collect_and_parse_cluster_events`",
)
def collect_cluster_events(
self,
start_timestamp: int,
end_timestamp: int,
local_timezone: str,
cluster_timezone: str = "UTC",
limit: int = 500,
namespace: str = None,
) -> Optional[str]:
"""
Collects cluster events querying `/api/v1/events`
Expand All @@ -2488,6 +2624,8 @@ def collect_cluster_events(
:param cluster_timezone: timezone of the remote cluster
:param limit: limit of the number of events to be fetched
from the cluster
:param namespace: Namespace from which the events must be
collected, if None all-namespaces will be selected
"""

Expand All @@ -2501,6 +2639,9 @@ def collect_cluster_events(
)

path = "/api/v1/events"
if namespace:
path = f"/api/v1/namespaces/{namespace}/events"

(data) = self.api_client.call_api(
path,
"GET",
Expand Down Expand Up @@ -2538,6 +2679,26 @@ def collect_cluster_events(
logging.error(str(e))
return None

def parse_events_from_file(
self, events_filename: str
) -> Optional[list[ClusterEvent]]:
if not events_filename or not os.path.exists(events_filename):
logging.error(f"events file do not exist {events_filename}")
return

events = []
with open(events_filename, "r") as jstream:
try:
json_obj = json.load(jstream)
for event in json_obj:
events.append(ClusterEvent(k8s_json_dict=event))
except Exception:
logging.error(
f"failed to parse events file: {events_filename}"
)

return events

def create_token_for_sa(
self, namespace: str, service_account: str, token_expiration=43200
) -> Optional[str]:
Expand Down
15 changes: 12 additions & 3 deletions src/krkn_lib/models/k8s/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from concurrent.futures import Future, ThreadPoolExecutor
from dataclasses import dataclass
from typing import Optional
from krkn_lib.version import __version__

import deprecation


@dataclass(frozen=True, order=False)
Expand Down Expand Up @@ -116,13 +119,21 @@ class Pod:
"""


@dataclass(frozen=True, order=False)
class LitmusChaosObject:
"""
Data class to hold information regarding
a custom object of litmus project
"""

@deprecation.deprecated(
deprecated_in="3.1.0",
removed_in="3.2.0",
current_version=__version__,
details="litmus support removed from krkn",
)
def __init__(self):
pass

kind: str
"""
Litmus Object Kind
Expand All @@ -149,7 +160,6 @@ class LitmusChaosObject:
"""


@dataclass(frozen=True, order=False)
class ChaosEngine(LitmusChaosObject):
"""
Data class to hold information
Expand All @@ -166,7 +176,6 @@ class ChaosEngine(LitmusChaosObject):
"""


@dataclass(frozen=True, order=False)
class ChaosResult(LitmusChaosObject):
"""
Data class to hold information
Expand Down
Loading

0 comments on commit aaa674a

Please sign in to comment.