2020import threading
2121from kubernetes import client , config , stream
2222from enum import Enum
23+ import re
2324
2425
2526class KubernetesPodExecState (Enum ):
2627 IDLE = 1
27- RUNNING = 2
28- FAILURE = 3
28+ WAITING_FOR_LIST_RUNNING = 2
29+ POD_NAME_PRESENT = 3
30+ RUNNING = 4
31+ FAILURE = 5
2932
3033
3134class KubernetesPodExec (BaseAction ):
3235
33- def __init__ (self , target : str , command : list , namespace : str , within_cluster : bool ):
36+ def __init__ (self , target : str , command : list , regex : bool , namespace : str , within_cluster : bool ):
3437 super ().__init__ ()
3538 self .target = target
3639 self .namespace = namespace
40+ self .regex = regex
3741 self .command = command
3842 self .within_cluster = within_cluster
3943 self .client = None
4044 self .reponse_queue = queue .Queue ()
4145 self .current_state = KubernetesPodExecState .IDLE
4246 self .output_queue = queue .Queue ()
47+ self .pod_list_request = None
48+ self .pod_name = None
4349
4450 def setup (self , ** kwargs ):
4551 if self .within_cluster :
@@ -50,18 +56,57 @@ def setup(self, **kwargs):
5056
5157 self .exec_thread = threading .Thread (target = self .pod_exec , daemon = True )
5258
53- def execute (self , target : str , command : list , namespace : str , within_cluster : bool ):
59+ def execute (self , target : str , command : list , regex : bool , namespace : str , within_cluster : bool ):
5460 if within_cluster != self .within_cluster :
5561 raise ValueError ("parameter 'within_cluster' is not allowed to change since initialization." )
5662 self .target = target
5763 self .namespace = namespace
5864 self .command = command
65+ self .regex = regex
66+ if self .pod_list_request :
67+ self .pod_list_request .cancel ()
68+ self .pod_name = None
5969 self .current_state = KubernetesPodExecState .IDLE
6070
61- def update (self ) -> py_trees .common .Status :
71+ def update (self ) -> py_trees .common .Status : # pylint: disable=too-many-return-statements
6272 if self .current_state == KubernetesPodExecState .IDLE :
73+ if self .regex :
74+ self .current_state = KubernetesPodExecState .WAITING_FOR_LIST_RUNNING
75+ self .feedback_message = f"Requesting list of pods in namespace '{ self .namespace } '" # pylint: disable= attribute-defined-outside-init
76+ self .pod_list_request = self .client .list_namespaced_pod (namespace = self .namespace , async_req = True )
77+ return py_trees .common .Status .RUNNING
78+ else :
79+ self .pod_name = self .target
80+ self .current_state = KubernetesPodExecState .POD_NAME_PRESENT
81+
82+ if self .current_state == KubernetesPodExecState .WAITING_FOR_LIST_RUNNING :
83+ if not self .pod_list_request .ready ():
84+ return py_trees .common .Status .RUNNING
85+ current_elements = []
86+ for i in self .pod_list_request .get ().items :
87+ current_elements .append (i .metadata .name )
88+
89+ found_element = None
90+ matched_elements = []
91+ for element in current_elements :
92+ if re .search (self .target , element ):
93+ matched_elements .append (element )
94+ if matched_elements :
95+ if len (matched_elements ) > 1 :
96+ 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
97+ return py_trees .common .Status .FAILURE
98+ found_element = matched_elements [0 ]
99+
100+ if found_element :
101+ self .pod_name = found_element
102+ self .current_state = KubernetesPodExecState .POD_NAME_PRESENT
103+ else :
104+ 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
105+ return py_trees .common .Status .FAILURE
106+
107+ if self .current_state == KubernetesPodExecState .POD_NAME_PRESENT :
63108 self .current_state = KubernetesPodExecState .RUNNING
64- self .feedback_message = f"Executing on pod '{ self .target } ': { self .command } ..." # pylint: disable= attribute-defined-outside-init
109+ self .feedback_message = f"Executing on pod '{ self .pod_name } ': { self .command } ..." # pylint: disable= attribute-defined-outside-init
65110 self .exec_thread .start ()
66111 return py_trees .common .Status .RUNNING
67112 elif self .current_state == KubernetesPodExecState .RUNNING :
@@ -82,7 +127,7 @@ def update(self) -> py_trees.common.Status:
82127
83128 def pod_exec (self ):
84129 resp = stream .stream (self .client .connect_get_namespaced_pod_exec ,
85- self .target ,
130+ self .pod_name ,
86131 self .namespace ,
87132 command = self .command ,
88133 stderr = True , stdin = False ,
0 commit comments