blob: 4ef8a49bc1adfecae1690d81e300dcabc3271bbd [file] [log] [blame]
garciadeblas96b94f52024-07-08 16:18:21 +02001#######################################################################################
2# Copyright ETSI Contributors and Others.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13# implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#######################################################################################
17
18
19from pyrage import x25519
20import yaml
21import base64
22
23
24def gather_age_key(cluster):
25 pubkey = cluster.get("age_pubkey")
26 privkey = cluster.get("age_privkey")
27 # return both public and private key
28 return pubkey, privkey
29
30
31def generate_age_key():
32 ident = x25519.Identity.generate()
33 # gets the public key
34 pubkey = ident.to_public()
35 # gets the private key
36 privkey = str(ident)
37 # return both public and private key
38 return pubkey, privkey
39
40
garciadeblasdde3a312024-09-17 13:25:06 +020041async def create_cluster(self, op_id, op_params, content):
garciadeblas96b94f52024-07-08 16:18:21 +020042 self.logger.info("Create cluster workflow Enter")
43 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
44
45 db_cluster = content["cluster"]
46 db_vim_account = content["vim_account"]
47
garciadeblas96b94f52024-07-08 16:18:21 +020048 workflow_template = "launcher-create-crossplane-cluster-and-bootstrap.j2"
49 workflow_name = f"create-cluster-{db_cluster['_id']}"
garciadeblas96b94f52024-07-08 16:18:21 +020050 cluster_name = db_cluster["git_name"].lower()
51
garciadeblas96b94f52024-07-08 16:18:21 +020052 # Get age key
53 public_key_new_cluster, private_key_new_cluster = gather_age_key(db_cluster)
54 self.logger.debug(f"public_key_new_cluster={public_key_new_cluster}")
55 self.logger.debug(f"private_key_new_cluster={private_key_new_cluster}")
56
57 # Test kubectl connection
58 self.logger.debug(self._kubectl._get_kubectl_version())
59
garciadeblasdde3a312024-09-17 13:25:06 +020060 # Create temporal secret with agekey
garciadeblas96b94f52024-07-08 16:18:21 +020061 secret_name = f"secret-age-{cluster_name}"
62 secret_namespace = "osm-workflows"
63 secret_key = "agekey"
64 secret_value = private_key_new_cluster
65 await self.create_secret(
66 secret_name,
67 secret_namespace,
68 secret_key,
69 secret_value,
70 )
71
72 # Additional params for the workflow
73 cluster_kustomization_name = cluster_name
74 osm_project_name = "osm_admin" # TODO: get project name from content
garciadeblasdde3a312024-09-17 13:25:06 +020075 vim_account_id = db_cluster["vim_account"]
76 providerconfig_name = f"{vim_account_id}-config"
77 vim_type = db_vim_account["vim_type"]
78 if vim_type == "azure":
79 cluster_type = "aks"
80 elif vim_type == "aws":
81 cluster_type = "eks"
82 elif vim_type == "gcp":
83 cluster_type = "gke"
garciadeblas96b94f52024-07-08 16:18:21 +020084 else:
garciadeblasdde3a312024-09-17 13:25:06 +020085 raise Exception("Not suitable VIM account to register cluster")
garciadeblas96b94f52024-07-08 16:18:21 +020086
87 # Render workflow
88 # workflow_kwargs = {
89 # "git_fleet_url": f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
90 # "git_sw_catalogs_url": f"{self._repo_base_url}/{self._repo_user}/sw-catalogs-osm.git",
91 # }
92 # manifest = self.render_jinja_template(
93 # workflow_template,
94 # output_file=None,
95 # **workflow_kwargs
96 # )
97 manifest = self.render_jinja_template(
98 workflow_template,
99 output_file=None,
100 workflow_name=workflow_name,
101 git_fleet_url=f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
102 git_sw_catalogs_url=f"{self._repo_base_url}/{self._repo_user}/sw-catalogs-osm.git",
103 cluster_name=cluster_name,
104 cluster_type=cluster_type,
105 cluster_kustomization_name=cluster_kustomization_name,
106 providerconfig_name=providerconfig_name,
107 public_key_mgmt=self._pubkey,
108 public_key_new_cluster=public_key_new_cluster,
109 secret_name_private_key_new_cluster=secret_name,
110 vm_size=db_cluster["node_size"],
111 node_count=db_cluster["node_count"],
112 k8s_version=db_cluster["k8s_version"],
113 cluster_location=db_cluster["region_name"],
114 osm_project_name=osm_project_name,
115 rg_name=db_cluster["resource_group"],
116 workflow_debug=self._workflow_debug,
117 workflow_dry_run=self._workflow_dry_run,
118 )
119 self.logger.debug(f"Workflow manifest: {manifest}")
120
121 # Submit workflow
122 self._kubectl.create_generic_object(
123 namespace="osm-workflows",
124 manifest_dict=yaml.safe_load(manifest),
125 api_group="argoproj.io",
126 api_plural="workflows",
127 api_version="v1alpha1",
128 )
129 return workflow_name
130
garciadeblas96b94f52024-07-08 16:18:21 +0200131
132async def update_cluster(self, op_id, op_params, content):
133 self.logger.info("Update cluster eks workflow Enter")
134 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
135
136 db_cluster = content["cluster"]
137 db_vim_account = content["vim_account"]
garciadeblas73cd5a22024-11-06 10:45:22 +0100138 cluster_name = db_cluster["git_name"].lower()
garciadeblas96b94f52024-07-08 16:18:21 +0200139
140 workflow_template = "launcher-update-crossplane-cluster.j2"
garciadeblas73cd5a22024-11-06 10:45:22 +0100141 workflow_name = f"update-cluster-{cluster_name}-{op_id}"
garciadeblas96b94f52024-07-08 16:18:21 +0200142 # cluster_name = db_cluster["name"].lower()
garciadeblas96b94f52024-07-08 16:18:21 +0200143
144 # Get age key
145 public_key_cluster, private_key_cluster = gather_age_key(db_cluster)
146 self.logger.debug(f"public_key_new_cluster={public_key_cluster}")
147 self.logger.debug(f"private_key_new_cluster={private_key_cluster}")
148
149 # Create secret with agekey
150 secret_name = f"secret-age-{cluster_name}"
151 secret_namespace = "osm-workflows"
152 secret_key = "agekey"
153 secret_value = private_key_cluster
154 await self.create_secret(
155 secret_name,
156 secret_namespace,
157 secret_key,
158 secret_value,
159 )
160
161 # Additional params for the workflow
162 cluster_kustomization_name = cluster_name
163 osm_project_name = "osm_admin" # TODO: get project name from db_cluster
164 vim_account_id = db_cluster["vim_account"]
165 providerconfig_name = f"{vim_account_id}-config"
166 vim_type = db_vim_account["vim_type"]
garciadeblas73cd5a22024-11-06 10:45:22 +0100167 vm_size = op_params.get("node_size", db_cluster["node_size"])
168 node_count = op_params.get("node_count", db_cluster["node_count"])
169 k8s_version = op_params.get("k8s_version", db_cluster["k8s_version"])
garciadeblas96b94f52024-07-08 16:18:21 +0200170 if vim_type == "azure":
171 cluster_type = "aks"
172 elif vim_type == "aws":
173 cluster_type = "eks"
174 elif vim_type == "gcp":
175 cluster_type = "gke"
176 else:
177 raise Exception("Not suitable VIM account to update cluster")
178
179 # Render workflow
180 manifest = self.render_jinja_template(
181 workflow_template,
182 output_file=None,
183 workflow_name=workflow_name,
184 git_fleet_url=f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
185 git_sw_catalogs_url=f"{self._repo_base_url}/{self._repo_user}/sw-catalogs-osm.git",
186 cluster_name=cluster_name,
187 cluster_type=cluster_type,
188 cluster_kustomization_name=cluster_kustomization_name,
189 providerconfig_name=providerconfig_name,
190 public_key_mgmt=self._pubkey,
191 public_key_new_cluster=public_key_cluster,
192 secret_name_private_key_new_cluster=secret_name,
garciadeblas73cd5a22024-11-06 10:45:22 +0100193 vm_size=vm_size,
194 node_count=node_count,
195 k8s_version=k8s_version,
garciadeblas96b94f52024-07-08 16:18:21 +0200196 cluster_location=db_cluster["region_name"],
197 osm_project_name=osm_project_name,
198 workflow_debug=self._workflow_debug,
199 workflow_dry_run=self._workflow_dry_run,
200 )
201 self.logger.info(manifest)
202
203 # Submit workflow
204 self._kubectl.create_generic_object(
205 namespace="osm-workflows",
206 manifest_dict=yaml.safe_load(manifest),
207 api_group="argoproj.io",
208 api_plural="workflows",
209 api_version="v1alpha1",
210 )
211 return workflow_name
212
213
214async def delete_cluster(self, op_id, op_params, content):
215 self.logger.info("Delete cluster workflow Enter")
216 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
217
218 db_cluster = content["cluster"]
219
220 workflow_template = "launcher-delete-cluster.j2"
221 workflow_name = f"delete-cluster-{db_cluster['_id']}"
222 # cluster_name = db_cluster["name"].lower()
223 cluster_name = db_cluster["git_name"].lower()
224
225 # Additional params for the workflow
226 cluster_kustomization_name = cluster_name
227 osm_project_name = "osm_admin" # TODO: get project name from DB
228
229 # Render workflow
230 manifest = self.render_jinja_template(
231 workflow_template,
232 output_file=None,
233 workflow_name=workflow_name,
234 git_fleet_url=f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
235 git_sw_catalogs_url=f"{self._repo_base_url}/{self._repo_user}/sw-catalogs-osm.git",
236 cluster_name=cluster_name,
237 cluster_kustomization_name=cluster_kustomization_name,
238 osm_project_name=osm_project_name,
239 workflow_debug=self._workflow_debug,
240 workflow_dry_run=self._workflow_dry_run,
241 )
242 self.logger.info(manifest)
243
244 # Submit workflow
245 self._kubectl.create_generic_object(
246 namespace="osm-workflows",
247 manifest_dict=yaml.safe_load(manifest),
248 api_group="argoproj.io",
249 api_plural="workflows",
250 api_version="v1alpha1",
251 )
252 return workflow_name
253
254
255async def register_cluster(self, op_id, op_params, content):
256 self.logger.info("Register cluster workflow Enter")
257 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
258
259 db_cluster = content["cluster"]
garciadeblas96b94f52024-07-08 16:18:21 +0200260 cluster_name = db_cluster["git_name"].lower()
261
garciadeblasdde3a312024-09-17 13:25:06 +0200262 workflow_template = "launcher-bootstrap-cluster.j2"
263 workflow_name = f"register-cluster-{db_cluster['_id']}"
264
265 # Get age key
266 public_key_new_cluster, private_key_new_cluster = gather_age_key(db_cluster)
267 self.logger.debug(f"public_key_new_cluster={public_key_new_cluster}")
268 self.logger.debug(f"private_key_new_cluster={private_key_new_cluster}")
269
270 # Create temporal secret with agekey
271 secret_name = f"secret-age-{cluster_name}"
272 secret_namespace = "osm-workflows"
273 secret_key = "agekey"
274 secret_value = private_key_new_cluster
garciadeblas96b94f52024-07-08 16:18:21 +0200275 await self.create_secret(
276 secret_name,
277 secret_namespace,
278 secret_key,
279 secret_value,
280 )
281
garciadeblasdde3a312024-09-17 13:25:06 +0200282 # Create secret with kubeconfig
283 secret_name2 = f"kubeconfig-{cluster_name}"
284 secret_namespace2 = "managed-resources"
285 secret_key2 = "kubeconfig"
286 secret_value2 = yaml.safe_dump(
287 db_cluster["credentials"], indent=4, default_flow_style=False, sort_keys=False
288 )
289 await self.create_secret(
290 secret_name2,
291 secret_namespace2,
292 secret_key2,
293 secret_value2,
294 )
295
296 # Additional params for the workflow
297 cluster_kustomization_name = cluster_name
298 osm_project_name = "osm_admin" # TODO: get project name from content
299 vim_account_id = db_cluster["vim_account"]
300 providerconfig_name = f"{vim_account_id}-config"
301
302 manifest = self.render_jinja_template(
303 workflow_template,
304 output_file=None,
305 workflow_name=workflow_name,
306 git_fleet_url=f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
307 git_sw_catalogs_url=f"{self._repo_base_url}/{self._repo_user}/sw-catalogs-osm.git",
308 cluster_name=cluster_name,
309 cluster_kustomization_name=cluster_kustomization_name,
310 providerconfig_name=providerconfig_name,
311 public_key_mgmt=self._pubkey,
312 public_key_new_cluster=public_key_new_cluster,
313 secret_name_private_key_new_cluster=secret_name,
314 osm_project_name=osm_project_name,
315 workflow_debug=self._workflow_debug,
316 workflow_dry_run=self._workflow_dry_run,
317 )
318 self.logger.debug(f"Workflow manifest: {manifest}")
319
320 # Submit workflow
321 self._kubectl.create_generic_object(
322 namespace="osm-workflows",
323 manifest_dict=yaml.safe_load(manifest),
324 api_group="argoproj.io",
325 api_plural="workflows",
326 api_version="v1alpha1",
327 )
garciadeblas96b94f52024-07-08 16:18:21 +0200328 return workflow_name
329
330
331async def deregister_cluster(self, op_id, op_params, content):
332 self.logger.info("Deregister cluster workflow Enter")
333 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
garciadeblasdde3a312024-09-17 13:25:06 +0200334
335 db_cluster = content["cluster"]
336 cluster_name = db_cluster["git_name"].lower()
337
338 workflow_template = "launcher-disconnect-flux-remote-cluster.j2"
339 workflow_name = f"deregister-cluster-{db_cluster['_id']}"
340
341 # Additional params for the workflow
342 cluster_kustomization_name = cluster_name
343 osm_project_name = "osm_admin" # TODO: get project name from DB
344
345 # Render workflow
346 manifest = self.render_jinja_template(
347 workflow_template,
348 output_file=None,
349 workflow_name=workflow_name,
350 git_fleet_url=f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
351 cluster_kustomization_name=cluster_kustomization_name,
352 osm_project_name=osm_project_name,
353 workflow_debug=self._workflow_debug,
354 workflow_dry_run=self._workflow_dry_run,
355 )
356 self.logger.info(manifest)
357
358 # Submit workflow
359 self._kubectl.create_generic_object(
360 namespace="osm-workflows",
361 manifest_dict=yaml.safe_load(manifest),
362 api_group="argoproj.io",
363 api_plural="workflows",
364 api_version="v1alpha1",
365 )
garciadeblas96b94f52024-07-08 16:18:21 +0200366 return workflow_name
367
368
369async def get_cluster_credentials(self, db_cluster):
370 """
371 returns the kubeconfig file of a K8s cluster in a dictionary
372 """
373 self.logger.info("Get cluster credentials Enter")
374 self.logger.info(f"Content: {db_cluster}")
375
376 secret_name = f"kubeconfig-{db_cluster['git_name'].lower()}"
377 secret_namespace = "managed-resources"
378 secret_key = "kubeconfig"
379
380 self.logger.info(f"Checking content of secret {secret_name} ...")
381 try:
382 returned_secret_data = await self._kubectl.get_secret_content(
383 name=secret_name,
384 namespace=secret_namespace,
385 )
386 returned_secret_value = base64.b64decode(
387 returned_secret_data[secret_key]
388 ).decode("utf-8")
389 return True, yaml.safe_load(returned_secret_value)
390 except Exception as e:
391 message = f"Not possible to get the credentials of the cluster. Exception: {e}"
392 self.logger.critical(message)
393 return False, message
394
395
garciadeblas28bff0f2024-09-16 12:53:07 +0200396async def clean_items_cluster_create(self, op_id, op_params, content):
397 self.logger.info("Clean items cluster_create Enter")
398 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
399 items = {
400 "secrets": [
401 {
402 "name": f"secret-age-{content['cluster']['git_name'].lower()}",
403 "namespace": "osm-workflows",
404 }
405 ]
406 }
407 try:
408 await self.clean_items(items)
409 return True, "OK"
410 except Exception as e:
411 return False, f"Error while cleaning items: {e}"
412
413
414async def clean_items_cluster_update(self, op_id, op_params, content):
415 self.logger.info("Clean items cluster_update Enter")
416 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
417 return await self.clean_items_cluster_create(op_id, op_params, content)
418
419
garciadeblasdde3a312024-09-17 13:25:06 +0200420async def clean_items_cluster_register(self, op_id, op_params, content):
421 self.logger.info("Clean items cluster_register Enter")
422 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
423 # Clean secrets
424 cluster_name = content["cluster"]["git_name"].lower()
425 items = {
426 "secrets": [
427 {
428 "name": f"secret-age-{cluster_name}",
429 "namespace": "osm-workflows",
430 },
431 ]
432 }
433
434 try:
435 await self.clean_items(items)
garciadeblas28d6e692024-10-15 13:14:39 +0200436 return True, "OK"
garciadeblasdde3a312024-09-17 13:25:06 +0200437 except Exception as e:
438 return False, f"Error while cleaning items: {e}"
439
440
garciadeblas96b94f52024-07-08 16:18:21 +0200441async def check_create_cluster(self, op_id, op_params, content):
442 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
garciadeblasb8976952024-10-18 11:36:15 +0200443 db_cluster = content["cluster"]
444 cluster_name = db_cluster["git_name"].lower()
445 cluster_kustomization_name = cluster_name
446 db_vim_account = content["vim_account"]
447 cloud_type = db_vim_account["vim_type"]
garciadeblasceaa19d2024-10-24 12:52:11 +0200448 nodepool_name = ""
449 if cloud_type == "aws":
450 nodepool_name = f"{cluster_name}-nodegroup"
451 cluster_name = f"{cluster_name}-cluster"
452 elif cloud_type == "gcp":
453 nodepool_name = f"nodepool-{cluster_name}"
garciadeblasb8976952024-10-18 11:36:15 +0200454 bootstrap = op_params.get("bootstrap", True)
garciadeblasceaa19d2024-10-24 12:52:11 +0200455 if cloud_type in ("azure", "gcp", "aws"):
456 checkings_list = [
457 {
458 "item": "kustomization",
459 "name": cluster_kustomization_name,
460 "namespace": "managed-resources",
461 "flag": "Ready",
462 "timeout": self._odu_checkloop_kustomization_timeout,
463 "enable": True,
464 },
465 {
466 "item": f"cluster_{cloud_type}",
467 "name": cluster_name,
468 "namespace": "",
469 "flag": "Synced",
470 "timeout": self._odu_checkloop_resource_timeout,
471 "enable": True,
472 },
473 {
474 "item": f"cluster_{cloud_type}",
475 "name": cluster_name,
476 "namespace": "",
477 "flag": "Ready",
478 "timeout": self._odu_checkloop_resource_timeout,
479 "enable": True,
480 },
481 {
482 "item": "kustomization",
483 "name": f"{cluster_kustomization_name}-bstrp-fluxctrl",
484 "namespace": "managed-resources",
485 "flag": "Ready",
486 "timeout": self._odu_checkloop_kustomization_timeout,
487 "enable": bootstrap,
488 },
489 ]
490 else:
garciadeblasb8976952024-10-18 11:36:15 +0200491 return False, "Not suitable VIM account to check cluster status"
garciadeblasceaa19d2024-10-24 12:52:11 +0200492 if nodepool_name:
493 nodepool_check = {
494 "item": f"nodepool_{cloud_type}",
495 "name": nodepool_name,
garciadeblas29d041a2024-10-22 12:54:22 +0200496 "namespace": "",
497 "flag": "Ready",
498 "timeout": self._odu_checkloop_resource_timeout,
499 "enable": True,
garciadeblasceaa19d2024-10-24 12:52:11 +0200500 }
501 checkings_list.insert(3, nodepool_check)
garciadeblas29d041a2024-10-22 12:54:22 +0200502 return await self.common_check_list(checkings_list)
garciadeblas96b94f52024-07-08 16:18:21 +0200503
504
505async def check_update_cluster(self, op_id, op_params, content):
506 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
507 return True, "OK"
508
509
510async def check_delete_cluster(self, op_id, op_params, content):
511 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
512 return True, "OK"
513
514
515async def check_register_cluster(self, op_id, op_params, content):
516 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
517 return True, "OK"
518
519
520async def check_deregister_cluster(self, op_id, op_params, content):
garciadeblasdde3a312024-09-17 13:25:06 +0200521 self.logger.info("check_deregister_cluster Enter")
garciadeblas96b94f52024-07-08 16:18:21 +0200522 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
garciadeblasdde3a312024-09-17 13:25:06 +0200523 # Clean secrets
524 self.logger.info("Cleaning kubeconfig")
525 cluster_name = content["cluster"]["git_name"].lower()
526 items = {
527 "secrets": [
528 {
529 "name": f"kubeconfig-{cluster_name}",
530 "namespace": "managed-resources",
531 },
532 ]
533 }
534
535 try:
536 await self.clean_items(items)
537 except Exception as e:
538 return False, f"Error while cleaning items: {e}"
garciadeblas96b94f52024-07-08 16:18:21 +0200539 return True, "OK"