1
1
import os
2
+ import yaml
3
+ import shutil
2
4
import subprocess
3
5
import base64
4
6
import time
@@ -45,20 +47,20 @@ def wait_for_capi_pods_ready(timeout=240, interval=15):
45
47
# Check pod phase and all containers readiness
46
48
if phase != "Running" or "false" in readiness_states :
47
49
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 } " )
49
51
else :
50
- print (f"Error fetching pods in { namespace } : { result .stderr } " )
52
+ logger . info (f"Error fetching pods in { namespace } : { result .stderr } " )
51
53
all_pods_ready = False
52
54
53
55
except subprocess .CalledProcessError as error :
54
- print (f"Error checking pods in { namespace } : { error } " )
56
+ logger . error (f"Error checking pods in { namespace } : { error } " )
55
57
all_pods_ready = False
56
58
57
59
if all_pods_ready :
58
- print ("All CAPI system pods are ready." )
60
+ logger . info ("All CAPI system pods are ready." )
59
61
return True
60
62
61
- print ("Waiting for all CAPI pods to become ready..." )
63
+ logger . info ("Waiting for all CAPI pods to become ready..." )
62
64
time .sleep (interval )
63
65
64
66
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):
93
95
# Check pod phase and all containers readiness
94
96
if phase != "Running" or "false" in readiness_states :
95
97
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 } " )
97
99
else :
98
- print (f"Error fetching pods in { cso_namespace } : { result .stderr } " )
100
+ logger . error (f"Error fetching pods in { cso_namespace } : { result .stderr } " )
99
101
all_pods_ready = False
100
102
101
103
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 } " )
103
105
all_pods_ready = False
104
106
105
107
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." )
107
109
return True
108
110
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..." )
110
112
time .sleep (interval )
111
113
112
114
raise TimeoutError (f"Timed out after { timeout } seconds waiting for CSO pods in 'cso-system' namespace to become ready." )
113
115
114
116
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 ):
116
118
"""
117
119
Waits for all pods in a specific namespace on a workload Kubernetes cluster to become ready.
118
120
119
121
: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 ).
121
123
:param kubeconfig_path: Path to the kubeconfig file for the target Kubernetes cluster.
122
124
:raises RuntimeError: If pods are not ready within the specified timeout.
123
125
"""
@@ -129,87 +131,111 @@ def wait_for_workload_pods_ready(namespace="kube-system", timeout=420, kubeconfi
129
131
130
132
# Run the command
131
133
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." )
133
135
134
136
except subprocess .CalledProcessError as error :
135
137
raise RuntimeError (f"Error waiting for pods in namespace '{ namespace } ' to become ready: { error } " )
136
138
137
139
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
+
138
215
class 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 )
206
229
207
- def _create_cluster (self ):
208
230
# Create the Kind cluster
209
- self .cluster = KindCluster (self . cluster_name )
231
+ self .cluster = KindCluster (name = cluster_name )
210
232
self .cluster .create ()
211
233
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
213
239
214
240
# Initialize clusterctl with OpenStack as the infrastructure provider
215
241
self ._run_subprocess (["clusterctl" , "init" , "--infrastructure" , "openstack" ], "Error during clusterctl init" )
@@ -241,34 +267,36 @@ def _create_cluster(self):
241
267
self ._retrieve_kubeconfig ()
242
268
243
269
# Wait for workload system pods to be ready
270
+ print (self .kubeconfig_cs_cluster_filename )
244
271
wait_for_workload_pods_ready (kubeconfig_path = self .kubeconfig_cs_cluster_filename )
245
272
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"
247
275
try :
248
276
# 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 } "
250
278
result = subprocess .run (check_cluster_command , shell = True , check = True , capture_output = True , text = True )
251
279
252
280
# Proceed with deletion only if the cluster exists
253
281
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 } "
255
283
self ._run_subprocess (delete_command , "Timeout while deleting the cluster" , shell = True )
256
284
257
285
except subprocess .CalledProcessError as error :
258
286
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." )
260
288
else :
261
289
raise RuntimeError (f"Error checking for cluster existence: { error } " )
262
290
263
291
# Delete kind cluster
264
- self .cluster = KindCluster (self . cluster_name )
292
+ self .cluster = KindCluster (cluster_name )
265
293
self .cluster .delete ()
266
294
267
295
# 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 )
272
300
273
301
def _apply_yaml_with_envsubst (self , yaml_file , error_msg ):
274
302
try :
@@ -320,7 +348,7 @@ def _wait_kcp_ready(self, kcp_name):
320
348
321
349
def _retrieve_kubeconfig (self ):
322
350
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 } "
324
352
)
325
353
self ._run_subprocess (kubeconfig_command , "Error retrieving kubeconfig" , shell = True )
326
354
0 commit comments