Skip to content
This repository was archived by the owner on Jul 16, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/libraries.rst
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,10 @@ Execute a command within a running pod
- ``list of string``
-
- Command to execute
* - ``regex``
- ``bool``
- ``false``
- Is the specified target a regular expression


``kubernetes_wait_for_network_policy_status()``
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,32 @@
import threading
from kubernetes import client, config, stream
from enum import Enum
import re


class KubernetesPodExecState(Enum):
IDLE = 1
RUNNING = 2
FAILURE = 3
WAITING_FOR_LIST_RUNNING = 2
POD_NAME_PRESENT = 3
RUNNING = 4
FAILURE = 5


class KubernetesPodExec(BaseAction):

def __init__(self, target: str, command: list, namespace: str, within_cluster: bool):
def __init__(self, target: str, command: list, regex: bool, namespace: str, within_cluster: bool):
super().__init__()
self.target = target
self.namespace = namespace
self.regex = regex
self.command = command
self.within_cluster = within_cluster
self.client = None
self.reponse_queue = queue.Queue()
self.current_state = KubernetesPodExecState.IDLE
self.output_queue = queue.Queue()
self.pod_list_request = None
self.pod_name = None

def setup(self, **kwargs):
if self.within_cluster:
Expand All @@ -50,18 +56,57 @@ def setup(self, **kwargs):

self.exec_thread = threading.Thread(target=self.pod_exec, daemon=True)

def execute(self, target: str, command: list, namespace: str, within_cluster: bool):
def execute(self, target: str, command: list, regex: bool, namespace: str, within_cluster: bool):
if within_cluster != self.within_cluster:
raise ValueError("parameter 'within_cluster' is not allowed to change since initialization.")
self.target = target
self.namespace = namespace
self.command = command
self.regex = regex
if self.pod_list_request:
self.pod_list_request.cancel()
self.pod_name = None
self.current_state = KubernetesPodExecState.IDLE

def update(self) -> py_trees.common.Status:
def update(self) -> py_trees.common.Status: # pylint: disable=too-many-return-statements
if self.current_state == KubernetesPodExecState.IDLE:
if self.regex:
self.current_state = KubernetesPodExecState.WAITING_FOR_LIST_RUNNING
self.feedback_message = f"Requesting list of pods in namespace '{self.namespace}'" # pylint: disable= attribute-defined-outside-init
self.pod_list_request = self.client.list_namespaced_pod(namespace=self.namespace, async_req=True)
return py_trees.common.Status.RUNNING
else:
self.pod_name = self.target
self.current_state = KubernetesPodExecState.POD_NAME_PRESENT

if self.current_state == KubernetesPodExecState.WAITING_FOR_LIST_RUNNING:
if not self.pod_list_request.ready():
return py_trees.common.Status.RUNNING
current_elements = []
for i in self.pod_list_request.get().items:
current_elements.append(i.metadata.name)

found_element = None
matched_elements = []
for element in current_elements:
if re.search(self.target, element):
matched_elements.append(element)
if matched_elements:
if len(matched_elements) > 1:
self.feedback_message = f"'{self.target}' regex identified more than one pod {', '.join(matched_elements)}. Only one element is supported!" # pylint: disable= attribute-defined-outside-init
return py_trees.common.Status.FAILURE
found_element = matched_elements[0]

if found_element:
self.pod_name = found_element
self.current_state = KubernetesPodExecState.POD_NAME_PRESENT
else:
self.feedback_message = f"'{self.target}' not found in list of available pods (namespace: '{self.namespace}'). Available: {', '.join(current_elements)}'" # pylint: disable= attribute-defined-outside-init
return py_trees.common.Status.FAILURE

if self.current_state == KubernetesPodExecState.POD_NAME_PRESENT:
self.current_state = KubernetesPodExecState.RUNNING
self.feedback_message = f"Executing on pod '{self.target}': {self.command}..." # pylint: disable= attribute-defined-outside-init
self.feedback_message = f"Executing on pod '{self.pod_name}': {self.command}..." # pylint: disable= attribute-defined-outside-init
self.exec_thread.start()
return py_trees.common.Status.RUNNING
elif self.current_state == KubernetesPodExecState.RUNNING:
Expand All @@ -82,7 +127,7 @@ def update(self) -> py_trees.common.Status:

def pod_exec(self):
resp = stream.stream(self.client.connect_get_namespaced_pod_exec,
self.target,
self.pod_name,
self.namespace,
command=self.command,
stderr=True, stdin=False,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ action kubernetes_patch_pod inherits kubernetes_base_action:

action kubernetes_pod_exec inherits kubernetes_base_action:
# execute a command within a running pod
target: string # pod to patch
command: list of string # command to execute
target: string # pod to execute the command in
command: list of string # command to execute
regex: bool = false # is the specified target a regular expression

action kubernetes_wait_for_network_policy_status inherits kubernetes_base_action:
# wait for a network-policy to reach the specified state
Expand Down