Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/bugfix/revert-wrongfully-deleted…
Browse files Browse the repository at this point in the history
…-files' into k8s_container_restart_count_annotation
  • Loading branch information
gAmitFrenkel committed Aug 14, 2024
2 parents 889ac13 + bb59f51 commit de246b0
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 1 deletion.
42 changes: 42 additions & 0 deletions granulate_utils/linux/containers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#
# Copyright (C) 2023 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import re
from typing import Optional

from psutil import Process

from granulate_utils.linux.process import get_process_cgroups

# ECS uses /ecs/uuid/container-id
# standard Docker uses /docker/container-id
# k8s uses /kubepods/{burstable,besteffort}/uuid/container-id
# there are some variations to the above formats, but generally, the container
# ID is always 64-hex digits.
CONTAINER_ID_PATTERN = re.compile(r"[a-f0-9]{64}")


def get_process_container_id(process: Process) -> Optional[str]:
"""
Gets the container ID of a running process, or None if not in a container.
:raises NoSuchProcess: If the process doesn't or no longer exists
"""
for proc_cgroup_line in get_process_cgroups(process):
found = CONTAINER_ID_PATTERN.findall(proc_cgroup_line.relative_path)
if found:
return found[-1]

return None
33 changes: 32 additions & 1 deletion granulate_utils/linux/ns.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@
from threading import Thread
from typing import Callable, Collection, List, Optional, TypeVar, Union

from psutil import NoSuchProcess, Process
from psutil import NoSuchProcess, Process, process_iter

from granulate_utils.exceptions import UnsupportedNamespaceError
from granulate_utils.linux import containers

T = TypeVar("T")

Expand Down Expand Up @@ -332,3 +333,33 @@ def resolve_host_path(process: Process, ns_path: str, from_ancestor: bool = True
Get a path in the host mount namespace pointing to path in process mount namespace.
"""
return resolve_proc_root_links(get_proc_root_path(process, from_ancestor=from_ancestor), ns_path)


def get_host_pid(nspid: int, container_id: str) -> Optional[int]:
assert len(container_id) == 64, f"Invalid container id {container_id!r}"

pid_namespace = ""
running_processes = list(process_iter())

# Get the pid namespace of the given container
for process in running_processes:
try:
if container_id == containers.get_process_container_id(process):
pid_namespace = os.readlink(f"/proc/{process.pid}/ns/pid")
break
except (FileNotFoundError, NoSuchProcess):
continue

if not pid_namespace:
return None

# Here we need to find the process by comparing pid namespaces and not by comparing cgroups, because
# technically a process that's running in a container pid namespace doesn't have to share its cgroup
for process in running_processes:
try:
if os.readlink(f"/proc/{process.pid}/ns/pid") == pid_namespace and get_process_nspid(process) == nspid:
return process.pid
except (FileNotFoundError, NoSuchProcess, PermissionError):
continue

return None
30 changes: 30 additions & 0 deletions granulate_utils/linux/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,33 @@ def search_for_process(filter: Callable[[psutil.Process], bool]) -> Iterator[psu
with contextlib.suppress(NoSuchProcess, AccessDenied):
if is_process_running(proc) and filter(proc):
yield proc


class ProcCgroupLine:
"""
The format of the line: hierarchy-ID:controller-list:relative-path
Example line: 1:cpu:/custom_cgroup
relative-path - the path of the cgroup the process belongs to, relative to the hierarchy mount point
e.g. /sys/fs/cgroup/memory on v1 or just the cgroups v2 mount on v2 e.g /sys/fs/cgroup.
"""

hier_id: str
controllers: List[str]
relative_path: str

def __init__(self, procfs_line: str):
hier_id, controller_list, relative_path = procfs_line.split(":", maxsplit=2)
self.hier_id = hier_id
self.controllers = controller_list.split(",")
self.relative_path = relative_path


def get_process_cgroups(process: Optional[psutil.Process] = None) -> List[ProcCgroupLine]:
"""
Get the cgroups of a process in [(hier id., controllers, path)] parsed form.
If process is None, gets the cgroups of the current process.
"""
process = process or psutil.Process()
text = read_proc_file(process, "cgroup").decode()
return [ProcCgroupLine(line) for line in text.splitlines()]

0 comments on commit de246b0

Please sign in to comment.