3232# Helper functions
3333def wait_for_pods (self , namespaces , timeout = 240 , interval = 15 , kubeconfig = None ):
3434 """
35- Waits for all pods in specified namespaces to reach the 'Running' state with all containers ready .
35+ Waits for all pods in specified namespaces to reach the condition 'Ready' .
3636
3737 :param namespaces: List of namespaces to check for pod readiness.
3838 :param timeout: Total time to wait in seconds before giving up.
3939 :param interval: Time to wait between checks in seconds.
40+ :param kubeconfig: Optional path to the kubeconfig file for the target Kubernetes cluster.
41+ :return: True if all pods are ready within the given timeout, raises TimeoutError otherwise.
4042 """
4143 start_time = time .time ()
4244
@@ -46,25 +48,16 @@ def wait_for_pods(self, namespaces, timeout=240, interval=15, kubeconfig=None):
4648 for namespace in namespaces :
4749 try :
4850 # Get pod status in the namespace
49- command = (
50- f"kubectl get pods -n { namespace } --kubeconfig { kubeconfig } "
51- f"-o=jsonpath='{{range .items[*]}}{{.metadata.name}} {{.status.phase}} {{range .status.containerStatuses[*]}}{{.ready}} {{end}}{{\" \\ n\" }}{{end}}'"
51+ wait_pods_command = (
52+ f"kubectl wait -n { namespace } --for=condition=Ready --timeout={ timeout } s pod --all"
5253 )
53- result = self ._run_subprocess (command , f"Error fetching pods in { namespace } " , shell = True , capture_output = True , text = True )
54-
55- if result .returncode == 0 :
56- pods_status = result .stdout .strip ().splitlines ()
57- for pod_status in pods_status :
58- pod_info = pod_status .split ()
59- pod_name , phase , * readiness_states = pod_info
60-
61- # Check pod phase and all containers readiness
62- if phase != "Running" or "false" in readiness_states :
63- all_pods_ready = False
64- logger .info (f"Pod { pod_name } in { namespace } is not ready. Phase: { phase } , Ready: { readiness_states } " )
65- else :
66- logger .error (f"Error fetching pods in { namespace } : { result .stderr } " )
54+ result = self ._run_subprocess (wait_pods_command , f"Error fetching pods in { namespace } " , shell = True , capture_output = True , text = True , kubeconfig = kubeconfig )
55+
56+ if result .returncode != 0 :
57+ logger .warning (f"Not all pods in namespace { namespace } are ready. Details: { result .stderr } " )
6758 all_pods_ready = False
59+ else :
60+ logger .info (f"All pods in namespace { namespace } are ready." )
6861
6962 except subprocess .CalledProcessError as error :
7063 logger .error (f"Error checking pods in { namespace } : { error } " )
@@ -80,34 +73,6 @@ def wait_for_pods(self, namespaces, timeout=240, interval=15, kubeconfig=None):
8073 raise TimeoutError (f"Timed out after { timeout } seconds waiting for pods in namespaces { namespaces } to become ready." )
8174
8275
83- def wait_for_workload_pods_ready (namespace = "kube-system" , timeout = 600 , kubeconfig_path = None , max_retries = 3 , delay = 30 ):
84- """
85- Waits for all pods in a specific namespace on a workload Kubernetes cluster to become ready.
86-
87- :param namespace: The Kubernetes namespace where pods are located (default is "kube-system").
88- :param timeout: The timeout in seconds to wait for pods to become ready (default is 600).
89- :param kubeconfig_path: Path to the kubeconfig file for the target Kubernetes cluster.
90- :raises RuntimeError: If pods are not ready within the specified timeout.
91- """
92- for attempt in range (max_retries ):
93- try :
94- kubeconfig_option = f"--kubeconfig { kubeconfig_path } " if kubeconfig_path else ""
95- wait_pods_command = (
96- f"kubectl wait -n { namespace } --for=condition=Ready --timeout={ timeout } s pod --all { kubeconfig_option } "
97- )
98-
99- # Run the command
100- subprocess .run (wait_pods_command , shell = True , check = True )
101- logger .info (f"All pods in namespace { namespace } in the workload Kubernetes cluster are ready." )
102- return True
103-
104- except subprocess .CalledProcessError as error :
105- logger .warning (f"Attempt { attempt + 1 } /{ max_retries } failed with error: { error } . Retrying in { delay } seconds..." )
106- time .sleep (delay )
107-
108- raise RuntimeError (f"Error waiting for pods in namespace '{ namespace } ' to become ready." )
109-
110-
11176def load_config (config_path ):
11277 """
11378 Loads the configuration from a YAML file.
@@ -122,6 +87,11 @@ def setup_environment_variables(self):
12287 """
12388 Constructs and returns a dictionary of required environment variables
12489 based on the configuration.
90+
91+ :raises ValueError: If the `GIT_ACCESS_TOKEN` environment variable is not set.
92+
93+ :return: A dictionary of required environment variables with necessary values and
94+ encodings for Kubernetes and Git-related configurations.
12595 """
12696 # Calculate values that need to be set dynamically
12797 if hasattr (self , 'cluster_version' ):
@@ -210,7 +180,8 @@ def create_cluster(self, cluster_name="scs-cluster", version=None, kubeconfig_fi
210180 self ._retrieve_kubeconfig (kubeconfig = self .kubeconfig_mgmnt_path )
211181
212182 # Wait for workload system pods to be ready
213- wait_for_workload_pods_ready (kubeconfig_path = self .kubeconfig_cs_cluster )
183+ # wait_for_workload_pods_ready(kubeconfig_path=self.kubeconfig_cs_cluster)
184+ wait_for_pods (self , ["kube-system" ], timeout = 600 , interval = 15 , kubeconfig = self .kubeconfig_cs_cluster )
214185
215186 def delete_cluster (self , cluster_name = None , kubeconfig_filepath = None ):
216187 self .cluster_name = cluster_name
@@ -242,6 +213,13 @@ def delete_cluster(self, cluster_name=None, kubeconfig_filepath=None):
242213 os .remove (self .kubeconfig_mgmnt_path )
243214
244215 def _apply_yaml_with_envsubst (self , yaml_file , error_msg , kubeconfig = None ):
216+ """
217+ Applies a Kubernetes YAML configuration file to the cluster, substituting environment variables as needed.
218+
219+ :param yaml_file: The name of the YAML file to apply.
220+ :param kubeconfig: Optional path to a kubeconfig file, which specifies which Kubernetes cluster
221+ to apply the YAML configuration to.
222+ """
245223 try :
246224 # Determine if the file is a local path or a URL
247225 if os .path .isfile (yaml_file ):
@@ -260,6 +238,13 @@ def _apply_yaml_with_envsubst(self, yaml_file, error_msg, kubeconfig=None):
260238 raise RuntimeError (f"{ error_msg } : { error } " )
261239
262240 def _get_kubeadm_control_plane_name (self , kubeconfig = None ):
241+ """
242+ Retrieves the name of the KubeadmControlPlane resource for the Kubernetes cluster.
243+
244+ :param kubeconfig: Optional path to the kubeconfig file for the target Kubernetes cluster.
245+
246+ :return: The name of the KubeadmControlPlane resource as a string.
247+ """
263248 max_retries = 6
264249 delay_between_retries = 10
265250 for _ in range (max_retries ):
@@ -281,6 +266,12 @@ def _get_kubeadm_control_plane_name(self, kubeconfig=None):
281266 raise RuntimeError ("Failed to get kubeadmcontrolplane name" )
282267
283268 def _wait_kcp_ready (self , kcp_name , kubeconfig = None ):
269+ """
270+ Waits for the specified KubeadmControlPlane resource to become 'Available'.
271+
272+ :param kcp_name: The name of the KubeadmControlPlane resource to check for availability.
273+ :param kubeconfig: Optional path to the kubeconfig file for the target Kubernetes cluster.
274+ """
284275 try :
285276 self ._run_subprocess (
286277 f"kubectl wait kubeadmcontrolplane/{ kcp_name } --for=condition=Available --timeout=600s" ,
@@ -292,12 +283,29 @@ def _wait_kcp_ready(self, kcp_name, kubeconfig=None):
292283 raise RuntimeError (f"Error waiting for kubeadmcontrolplane to be ready: { error } " )
293284
294285 def _retrieve_kubeconfig (self , kubeconfig = None ):
286+ """
287+ Retrieves the kubeconfig for the specified cluster and saves it to a local file.
288+
289+ :param kubeconfig: Optional path to the kubeconfig file for the target Kubernetes cluster.
290+ """
295291 kubeconfig_command = (
296292 f"sudo -E clusterctl get kubeconfig { self .cluster_name } > { self .kubeconfig_cs_cluster } "
297293 )
298294 self ._run_subprocess (kubeconfig_command , "Error retrieving kubeconfig" , shell = True , kubeconfig = kubeconfig )
299295
300296 def _run_subprocess (self , command , error_msg , shell = False , capture_output = False , text = False , kubeconfig = None ):
297+ """
298+ Executes a subprocess command with the specified environment variables and parameters.
299+
300+ :param command: The shell command to be executed. This can be a string or a list of arguments to pass to the subprocess.
301+ :param error_msg: A custom error message to be logged and raised if the subprocess fails.
302+ :param shell: Whether to execute the command through the shell (default: `False`).
303+ :param capture_output: Whether to capture the command's standard output and standard error (default: `False`).
304+ :param text: Whether to treat the command's output and error as text (default: `False`).
305+ :param kubeconfig: Optional path to the kubeconfig file for the target Kubernetes cluster.
306+
307+ :return: The result of the `subprocess.run` command
308+ """
301309 try :
302310 env = setup_environment_variables (self )
303311 env ['PATH' ] = f'/usr/local/bin:/usr/bin:{ self .working_directory } '
0 commit comments