11import os
2+ import yaml
3+ import shutil
24import subprocess
35import base64
46import time
@@ -45,20 +47,20 @@ def wait_for_capi_pods_ready(timeout=240, interval=15):
4547 # Check pod phase and all containers readiness
4648 if phase != "Running" or "false" in readiness_states :
4749 all_pods_ready = False
48- print (f"Pod { pod_name } in { namespace } is not ready. Phase: { phase } , Ready: { readiness_states } " )
50+ logger . info (f"Pod { pod_name } in { namespace } is not ready. Phase: { phase } , Ready: { readiness_states } " )
4951 else :
50- print (f"Error fetching pods in { namespace } : { result .stderr } " )
52+ logger . info (f"Error fetching pods in { namespace } : { result .stderr } " )
5153 all_pods_ready = False
5254
5355 except subprocess .CalledProcessError as error :
54- print (f"Error checking pods in { namespace } : { error } " )
56+ logger . error (f"Error checking pods in { namespace } : { error } " )
5557 all_pods_ready = False
5658
5759 if all_pods_ready :
58- print ("All CAPI system pods are ready." )
60+ logger . info ("All CAPI system pods are ready." )
5961 return True
6062
61- print ("Waiting for all CAPI pods to become ready..." )
63+ logger . info ("Waiting for all CAPI pods to become ready..." )
6264 time .sleep (interval )
6365
6466 raise TimeoutError (f"Timed out after { timeout } seconds waiting for CAPI and CAPO system pods to become ready." )
@@ -93,31 +95,31 @@ def wait_for_cso_pods_ready(timeout=240, interval=15):
9395 # Check pod phase and all containers readiness
9496 if phase != "Running" or "false" in readiness_states :
9597 all_pods_ready = False
96- print (f"Pod { pod_name } in { cso_namespace } is not ready. Phase: { phase } , Ready: { readiness_states } " )
98+ logger . info (f"Pod { pod_name } in { cso_namespace } is not ready. Phase: { phase } , Ready: { readiness_states } " )
9799 else :
98- print (f"Error fetching pods in { cso_namespace } : { result .stderr } " )
100+ logger . error (f"Error fetching pods in { cso_namespace } : { result .stderr } " )
99101 all_pods_ready = False
100102
101103 except subprocess .CalledProcessError as error :
102- print (f"Error checking pods in { cso_namespace } : { error } " )
104+ logger . error (f"Error checking pods in { cso_namespace } : { error } " )
103105 all_pods_ready = False
104106
105107 if all_pods_ready :
106- print ("All CSO pods in 'cso-system' namespace are ready." )
108+ logger . info ("All CSO pods in 'cso-system' namespace are ready." )
107109 return True
108110
109- print ("Waiting for CSO pods in 'cso-system' namespace to become ready..." )
111+ logger . info ("Waiting for CSO pods in 'cso-system' namespace to become ready..." )
110112 time .sleep (interval )
111113
112114 raise TimeoutError (f"Timed out after { timeout } seconds waiting for CSO pods in 'cso-system' namespace to become ready." )
113115
114116
115- def wait_for_workload_pods_ready (namespace = "kube-system" , timeout = 420 , kubeconfig_path = None ):
117+ def wait_for_workload_pods_ready (namespace = "kube-system" , timeout = 600 , kubeconfig_path = None ):
116118 """
117119 Waits for all pods in a specific namespace on a workload Kubernetes cluster to become ready.
118120
119121 :param namespace: The Kubernetes namespace where pods are located (default is "kube-system").
120- :param timeout: The timeout in seconds to wait for pods to become ready (default is 420 ).
122+ :param timeout: The timeout in seconds to wait for pods to become ready (default is 600 ).
121123 :param kubeconfig_path: Path to the kubeconfig file for the target Kubernetes cluster.
122124 :raises RuntimeError: If pods are not ready within the specified timeout.
123125 """
@@ -129,87 +131,111 @@ def wait_for_workload_pods_ready(namespace="kube-system", timeout=420, kubeconfi
129131
130132 # Run the command
131133 subprocess .run (wait_pods_command , shell = True , check = True )
132- print ( f "All pods in namespace '{ namespace } ' in the workload Kubernetes cluster are ready." )
134+ logger . info ( "All pods in namespace '{namespace}' in the workload Kubernetes cluster are ready." )
133135
134136 except subprocess .CalledProcessError as error :
135137 raise RuntimeError (f"Error waiting for pods in namespace '{ namespace } ' to become ready: { error } " )
136138
137139
140+ def load_config (config_path ):
141+ """
142+ Loads the configuration from a YAML file.
143+ """
144+ if not os .path .exists (config_path ):
145+ raise FileNotFoundError (f"Configuration file { config_path } not found." )
146+
147+ with open (config_path , 'r' ) as file :
148+ config = yaml .safe_load (file ) or {}
149+ return config
150+
151+
152+ def setup_environment_variables (self ):
153+ # Cluster Stack Parameters
154+ self .clouds_yaml_path = self .config .get ('clouds_yaml_path' , '~/.config/openstack/clouds.yaml' )
155+ self .cs_k8s_version = self .cluster_version
156+ self .cs_name = self .config .get ('cs_name' , 'scs' )
157+ self .cs_version = self .config .get ('cs_version' , 'v1' )
158+ self .cs_channel = self .config .get ('cs_channel' , 'stable' )
159+ self .cs_cloudname = self .config .get ('cs_cloudname' , 'openstack' )
160+ self .cs_secretname = self .cs_cloudname
161+
162+ # CSP-related variables and additional cluster configuration
163+ self .kubeconfig_cs_cluster_filename = f"kubeconfig-{ self .cluster_name } .yaml"
164+ self .cs_class_name = f"openstack-{ self .cs_name } -{ str (self .cs_k8s_version ).replace ('.' , '-' )} -{ self .cs_version } "
165+ self .cs_namespace = self .config .get ("cs_namespace" , "default" )
166+ self .cs_pod_cidr = self .config .get ('cs_pod_cidr' , '192.168.0.0/16' )
167+ self .cs_service_cidr = self .config .get ('cs_service_cidr' , '10.96.0.0/12' )
168+ self .cs_external_id = self .config .get ('cs_external_id' , 'ebfe5546-f09f-4f42-ab54-094e457d42ec' )
169+ self .cs_k8s_patch_version = self .config .get ('cs_k8s_patch_version' , '6' )
170+
171+ if not self .clouds_yaml_path :
172+ raise ValueError ("CLOUDS_YAML_PATH environment variable not set." )
173+
174+ required_env = {
175+ 'CLUSTER_TOPOLOGY' : 'true' ,
176+ 'EXP_CLUSTER_RESOURCE_SET' : 'true' ,
177+ 'EXP_RUNTIME_SDK' : 'true' ,
178+ 'CS_NAME' : self .cs_name ,
179+ 'CS_K8S_VERSION' : self .cs_k8s_version ,
180+ 'CS_VERSION' : self .cs_version ,
181+ 'CS_CHANNEL' : self .cs_channel ,
182+ 'CS_CLOUDNAME' : self .cs_cloudname ,
183+ 'CS_SECRETNAME' : self .cs_secretname ,
184+ 'CS_CLASS_NAME' : self .cs_class_name ,
185+ 'CS_NAMESPACE' : self .cs_namespace ,
186+ 'CS_POD_CIDR' : self .cs_pod_cidr ,
187+ 'CS_SERVICE_CIDR' : self .cs_service_cidr ,
188+ 'CS_EXTERNAL_ID' : self .cs_external_id ,
189+ 'CS_K8S_PATCH_VERSION' : self .cs_k8s_patch_version ,
190+ 'CS_CLUSTER_NAME' : self .cluster_name ,
191+ }
192+ # Update the environment variables
193+ os .environ .update ({key : str (value ) for key , value in required_env .items ()})
194+
195+
196+ def setup_git_env (self ):
197+ # Setup Git environment variables
198+ git_provider = self .config .get ('git_provider' , 'github' )
199+ git_org_name = self .config .get ('git_org_name' , 'SovereignCloudStack' )
200+ git_repo_name = self .config .get ('git_repo_name' , 'cluster-stacks' )
201+
202+ os .environ .update ({
203+ 'GIT_PROVIDER_B64' : base64 .b64encode (git_provider .encode ()).decode ('utf-8' ),
204+ 'GIT_ORG_NAME_B64' : base64 .b64encode (git_org_name .encode ()).decode ('utf-8' ),
205+ 'GIT_REPOSITORY_NAME_B64' : base64 .b64encode (git_repo_name .encode ()).decode ('utf-8' )
206+ })
207+
208+ git_access_token = os .getenv ('GIT_ACCESS_TOKEN' )
209+ if git_access_token :
210+ os .environ ['GIT_ACCESS_TOKEN_B64' ] = base64 .b64encode (git_access_token .encode ()).decode ('utf-8' )
211+ else :
212+ raise ValueError ("GIT_ACCESS_TOKEN environment variable not set." )
213+
214+
138215class PluginClusterStacks (KubernetesClusterPlugin ):
139- def __init__ (self , config = None ):
140- super ().__init__ (config )
141- self .cluster_info = config if config else {}
142- self ._setup_environment_variables ()
143- self ._setup_git_env ()
144-
145- def _setup_environment_variables (self ):
146- # Cluster Stack Parameters
147- self .clouds_yaml_path = self .cluster_info .get ('clouds_yaml_path' )
148- self .cs_k8s_version = self .cluster_info .get ('cs_k8s_version' , '1.29' )
149- self .cs_name = self .cluster_info .get ('cs_name' , 'scs' )
150- self .cs_version = self .cluster_info .get ('cs_version' , 'v1' )
151- self .cs_channel = self .cluster_info .get ('cs_channel' , 'stable' )
152- self .cs_cloudname = self .cluster_info .get ('cs_cloudname' , 'openstack' )
153- self .cs_secretname = self .cs_cloudname
154-
155- # CSP-related variables and additional cluster configuration
156- self .cs_cluster_name = self .cluster_info .get ('cs_cluster_name' , 'cs-cluster' )
157- self .kubeconfig_cs_cluster_filename = f"kubeconfig-{ self .cs_cluster_name } "
158- self .cs_class_name = f"openstack-{ self .cs_name } -{ str (self .cs_k8s_version ).replace ('.' , '-' )} -{ self .cs_version } "
159- self .cs_namespace = os .getenv ("CS_NAMESPACE" , "default" )
160- self .cs_pod_cidr = self .cluster_info .get ('cs_pod_cidr' , '192.168.0.0/16' )
161- self .cs_service_cidr = self .cluster_info .get ('cs_service_cidr' , '10.96.0.0/12' )
162- self .cs_external_id = self .cluster_info .get ('cs_external_id' , 'ebfe5546-f09f-4f42-ab54-094e457d42ec' )
163- self .cs_k8s_patch_version = self .cluster_info .get ('cs_k8s_patch_version' , '6' )
164-
165- if not self .clouds_yaml_path :
166- raise ValueError ("CLOUDS_YAML_PATH environment variable not set." )
167-
168- required_env = {
169- 'CLUSTER_TOPOLOGY' : 'true' ,
170- 'EXP_CLUSTER_RESOURCE_SET' : 'true' ,
171- 'EXP_RUNTIME_SDK' : 'true' ,
172- 'CS_NAME' : self .cs_name ,
173- 'CS_K8S_VERSION' : self .cs_k8s_version ,
174- 'CS_VERSION' : self .cs_version ,
175- 'CS_CHANNEL' : self .cs_channel ,
176- 'CS_CLOUDNAME' : self .cs_cloudname ,
177- 'CS_SECRETNAME' : self .cs_secretname ,
178- 'CS_CLASS_NAME' : self .cs_class_name ,
179- 'CS_NAMESPACE' : self .cs_namespace ,
180- 'CS_POD_CIDR' : self .cs_pod_cidr ,
181- 'CS_SERVICE_CIDR' : self .cs_service_cidr ,
182- 'CS_EXTERNAL_ID' : self .cs_external_id ,
183- 'CS_K8S_PATCH_VERSION' : self .cs_k8s_patch_version ,
184- 'CS_CLUSTER_NAME' : self .cs_cluster_name ,
185- }
186- # Update the environment variables
187- os .environ .update ({key : str (value ) for key , value in required_env .items ()})
188-
189- def _setup_git_env (self ):
190- # Setup Git environment variables
191- git_provider = self .cluster_info .get ('git_provider' , 'github' )
192- git_org_name = self .cluster_info .get ('git_org_name' , 'SovereignCloudStack' )
193- git_repo_name = self .cluster_info .get ('git_repo_name' , 'cluster-stacks' )
194-
195- os .environ .update ({
196- 'GIT_PROVIDER_B64' : base64 .b64encode (git_provider .encode ()).decode ('utf-8' ),
197- 'GIT_ORG_NAME_B64' : base64 .b64encode (git_org_name .encode ()).decode ('utf-8' ),
198- 'GIT_REPOSITORY_NAME_B64' : base64 .b64encode (git_repo_name .encode ()).decode ('utf-8' )
199- })
200-
201- git_access_token = os .getenv ('GIT_ACCESS_TOKEN' )
202- if git_access_token :
203- os .environ ['GIT_ACCESS_TOKEN_B64' ] = base64 .b64encode (git_access_token .encode ()).decode ('utf-8' )
204- else :
205- raise ValueError ("GIT_ACCESS_TOKEN environment variable not set." )
216+ def __init__ (self , config_file = None ):
217+ self .config = load_config (config_file ) if config_file else {}
218+ logger .debug (self .config )
219+ self .working_directory = os .getcwd ()
220+ logger .debug (f"Working from { self .working_directory } " )
221+
222+ def create_cluster (self , cluster_name = "scs-cluster" , version = None , kubeconfig_filepath = None ):
223+ self .cluster_name = cluster_name
224+ self .cluster_version = version
225+
226+ # Setup variables
227+ setup_environment_variables (self )
228+ setup_git_env (self )
206229
207- def _create_cluster (self ):
208230 # Create the Kind cluster
209- self .cluster = KindCluster (self . cluster_name )
231+ self .cluster = KindCluster (name = cluster_name )
210232 self .cluster .create ()
211233 self .kubeconfig = str (self .cluster .kubeconfig_path .resolve ())
212- os .environ ['KUBECONFIG' ] = self .kubeconfig
234+ if kubeconfig_filepath :
235+ shutil .move (self .kubeconfig , kubeconfig_filepath )
236+ else :
237+ kubeconfig_filepath = str (self .kubeconfig )
238+ os .environ ['KUBECONFIG' ] = kubeconfig_filepath
213239
214240 # Initialize clusterctl with OpenStack as the infrastructure provider
215241 self ._run_subprocess (["clusterctl" , "init" , "--infrastructure" , "openstack" ], "Error during clusterctl init" )
@@ -241,34 +267,36 @@ def _create_cluster(self):
241267 self ._retrieve_kubeconfig ()
242268
243269 # Wait for workload system pods to be ready
270+ print (self .kubeconfig_cs_cluster_filename )
244271 wait_for_workload_pods_ready (kubeconfig_path = self .kubeconfig_cs_cluster_filename )
245272
246- def _delete_cluster (self ):
273+ def delete_cluster (self , cluster_name = None , kubeconfig_filepath = None ):
274+ kubeconfig_cs_cluster_filename = f"kubeconfig-{ cluster_name } .yaml"
247275 try :
248276 # Check if the cluster exists
249- check_cluster_command = f"kubectl get cluster { self . cs_cluster_name } --kubeconfig { self . cluster_info . get ( 'kubeconfig' ) } "
277+ check_cluster_command = f"kubectl get cluster { cluster_name } --kubeconfig { kubeconfig_filepath } "
250278 result = subprocess .run (check_cluster_command , shell = True , check = True , capture_output = True , text = True )
251279
252280 # Proceed with deletion only if the cluster exists
253281 if result .returncode == 0 :
254- delete_command = f"kubectl delete cluster { self . cs_cluster_name } --timeout=600s --kubeconfig { self . cluster_info . get ( 'kubeconfig' ) } "
282+ delete_command = f"kubectl delete cluster { cluster_name } --timeout=600s --kubeconfig { kubeconfig_filepath } "
255283 self ._run_subprocess (delete_command , "Timeout while deleting the cluster" , shell = True )
256284
257285 except subprocess .CalledProcessError as error :
258286 if "NotFound" in error .stderr :
259- logger .info (f"Cluster { self . cs_cluster_name } not found. Skipping deletion." )
287+ logger .info (f"Cluster { cluster_name } not found. Skipping deletion." )
260288 else :
261289 raise RuntimeError (f"Error checking for cluster existence: { error } " )
262290
263291 # Delete kind cluster
264- self .cluster = KindCluster (self . cluster_name )
292+ self .cluster = KindCluster (cluster_name )
265293 self .cluster .delete ()
266294
267295 # Remove kubeconfigs
268- if os .path .exists (self . kubeconfig_cs_cluster_filename ):
269- os .remove (self . kubeconfig_cs_cluster_filename )
270- if os .path .exists (self . cluster_info . get ( 'kubeconfig' ) ):
271- os .remove (self . cluster_info . get ( 'kubeconfig' ) )
296+ if os .path .exists (kubeconfig_cs_cluster_filename ):
297+ os .remove (kubeconfig_cs_cluster_filename )
298+ if os .path .exists (kubeconfig_filepath ):
299+ os .remove (kubeconfig_filepath )
272300
273301 def _apply_yaml_with_envsubst (self , yaml_file , error_msg ):
274302 try :
@@ -320,7 +348,7 @@ def _wait_kcp_ready(self, kcp_name):
320348
321349 def _retrieve_kubeconfig (self ):
322350 kubeconfig_command = (
323- f"clusterctl get kubeconfig { self .cs_cluster_name } > { self .kubeconfig_cs_cluster_filename } "
351+ f"clusterctl get kubeconfig { self .cluster_name } > { self .kubeconfig_cs_cluster_filename } "
324352 )
325353 self ._run_subprocess (kubeconfig_command , "Error retrieving kubeconfig" , shell = True )
326354
0 commit comments