blob: bfaad75b7d57ee84e58ab728ed0a7452f5e0f007 [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
garciadeblasf1458032024-09-17 13:25:06 +020041async def create_cluster(self, op_id, op_params, content):
garciadeblas1f71bab2024-10-22 14:04:36 +020042 self.logger.info(f"create_cluster Enter. Operation {op_id}. Params: {op_params}")
43 # self.logger.debug(f"Content: {content}")
garciadeblas96b94f52024-07-08 16:18:21 +020044
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
garciadeblasf1458032024-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
garciadeblasf1458032024-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"]
garciadeblasfe8449c2024-11-06 12:56:33 +010078 if db_cluster.get("bootstrap", True):
79 skip_bootstrap = "false"
80 else:
81 skip_bootstrap = "true"
garciadeblasf1458032024-09-17 13:25:06 +020082 if vim_type == "azure":
83 cluster_type = "aks"
84 elif vim_type == "aws":
85 cluster_type = "eks"
86 elif vim_type == "gcp":
87 cluster_type = "gke"
garciadeblas96b94f52024-07-08 16:18:21 +020088 else:
garciadeblasf1458032024-09-17 13:25:06 +020089 raise Exception("Not suitable VIM account to register cluster")
garciadeblas96b94f52024-07-08 16:18:21 +020090
91 # Render workflow
92 # workflow_kwargs = {
93 # "git_fleet_url": f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
94 # "git_sw_catalogs_url": f"{self._repo_base_url}/{self._repo_user}/sw-catalogs-osm.git",
95 # }
96 # manifest = self.render_jinja_template(
97 # workflow_template,
98 # output_file=None,
99 # **workflow_kwargs
100 # )
101 manifest = self.render_jinja_template(
102 workflow_template,
103 output_file=None,
104 workflow_name=workflow_name,
105 git_fleet_url=f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
106 git_sw_catalogs_url=f"{self._repo_base_url}/{self._repo_user}/sw-catalogs-osm.git",
107 cluster_name=cluster_name,
108 cluster_type=cluster_type,
109 cluster_kustomization_name=cluster_kustomization_name,
110 providerconfig_name=providerconfig_name,
111 public_key_mgmt=self._pubkey,
112 public_key_new_cluster=public_key_new_cluster,
113 secret_name_private_key_new_cluster=secret_name,
114 vm_size=db_cluster["node_size"],
115 node_count=db_cluster["node_count"],
116 k8s_version=db_cluster["k8s_version"],
117 cluster_location=db_cluster["region_name"],
118 osm_project_name=osm_project_name,
119 rg_name=db_cluster["resource_group"],
garciadeblasfe8449c2024-11-06 12:56:33 +0100120 skip_bootstrap=skip_bootstrap,
garciadeblas96b94f52024-07-08 16:18:21 +0200121 workflow_debug=self._workflow_debug,
122 workflow_dry_run=self._workflow_dry_run,
123 )
124 self.logger.debug(f"Workflow manifest: {manifest}")
125
126 # Submit workflow
127 self._kubectl.create_generic_object(
128 namespace="osm-workflows",
129 manifest_dict=yaml.safe_load(manifest),
130 api_group="argoproj.io",
131 api_plural="workflows",
132 api_version="v1alpha1",
133 )
134 return workflow_name
135
garciadeblas96b94f52024-07-08 16:18:21 +0200136
137async def update_cluster(self, op_id, op_params, content):
garciadeblas1f71bab2024-10-22 14:04:36 +0200138 self.logger.info(f"update_cluster Enter. Operation {op_id}. Params: {op_params}")
139 # self.logger.debug(f"Content: {content}")
garciadeblas96b94f52024-07-08 16:18:21 +0200140
141 db_cluster = content["cluster"]
142 db_vim_account = content["vim_account"]
garciadeblas618c5162024-11-06 10:45:22 +0100143 cluster_name = db_cluster["git_name"].lower()
garciadeblas96b94f52024-07-08 16:18:21 +0200144
145 workflow_template = "launcher-update-crossplane-cluster.j2"
garciadeblas618c5162024-11-06 10:45:22 +0100146 workflow_name = f"update-cluster-{cluster_name}-{op_id}"
garciadeblas96b94f52024-07-08 16:18:21 +0200147 # cluster_name = db_cluster["name"].lower()
garciadeblas96b94f52024-07-08 16:18:21 +0200148
149 # Get age key
150 public_key_cluster, private_key_cluster = gather_age_key(db_cluster)
151 self.logger.debug(f"public_key_new_cluster={public_key_cluster}")
152 self.logger.debug(f"private_key_new_cluster={private_key_cluster}")
153
154 # Create secret with agekey
155 secret_name = f"secret-age-{cluster_name}"
156 secret_namespace = "osm-workflows"
157 secret_key = "agekey"
158 secret_value = private_key_cluster
159 await self.create_secret(
160 secret_name,
161 secret_namespace,
162 secret_key,
163 secret_value,
164 )
165
166 # Additional params for the workflow
167 cluster_kustomization_name = cluster_name
168 osm_project_name = "osm_admin" # TODO: get project name from db_cluster
169 vim_account_id = db_cluster["vim_account"]
170 providerconfig_name = f"{vim_account_id}-config"
171 vim_type = db_vim_account["vim_type"]
garciadeblas618c5162024-11-06 10:45:22 +0100172 vm_size = op_params.get("node_size", db_cluster["node_size"])
173 node_count = op_params.get("node_count", db_cluster["node_count"])
174 k8s_version = op_params.get("k8s_version", db_cluster["k8s_version"])
garciadeblas96b94f52024-07-08 16:18:21 +0200175 if vim_type == "azure":
176 cluster_type = "aks"
177 elif vim_type == "aws":
178 cluster_type = "eks"
179 elif vim_type == "gcp":
180 cluster_type = "gke"
181 else:
182 raise Exception("Not suitable VIM account to update cluster")
183
184 # Render workflow
185 manifest = self.render_jinja_template(
186 workflow_template,
187 output_file=None,
188 workflow_name=workflow_name,
189 git_fleet_url=f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
190 git_sw_catalogs_url=f"{self._repo_base_url}/{self._repo_user}/sw-catalogs-osm.git",
191 cluster_name=cluster_name,
192 cluster_type=cluster_type,
193 cluster_kustomization_name=cluster_kustomization_name,
194 providerconfig_name=providerconfig_name,
195 public_key_mgmt=self._pubkey,
196 public_key_new_cluster=public_key_cluster,
197 secret_name_private_key_new_cluster=secret_name,
garciadeblas618c5162024-11-06 10:45:22 +0100198 vm_size=vm_size,
199 node_count=node_count,
200 k8s_version=k8s_version,
garciadeblas96b94f52024-07-08 16:18:21 +0200201 cluster_location=db_cluster["region_name"],
202 osm_project_name=osm_project_name,
203 workflow_debug=self._workflow_debug,
204 workflow_dry_run=self._workflow_dry_run,
205 )
206 self.logger.info(manifest)
207
208 # Submit workflow
209 self._kubectl.create_generic_object(
210 namespace="osm-workflows",
211 manifest_dict=yaml.safe_load(manifest),
212 api_group="argoproj.io",
213 api_plural="workflows",
214 api_version="v1alpha1",
215 )
216 return workflow_name
217
218
219async def delete_cluster(self, op_id, op_params, content):
garciadeblas1f71bab2024-10-22 14:04:36 +0200220 self.logger.info(f"delete_cluster Enter. Operation {op_id}. Params: {op_params}")
221 # self.logger.debug(f"Content: {content}")
garciadeblas96b94f52024-07-08 16:18:21 +0200222
223 db_cluster = content["cluster"]
224
225 workflow_template = "launcher-delete-cluster.j2"
226 workflow_name = f"delete-cluster-{db_cluster['_id']}"
227 # cluster_name = db_cluster["name"].lower()
228 cluster_name = db_cluster["git_name"].lower()
229
230 # Additional params for the workflow
231 cluster_kustomization_name = cluster_name
232 osm_project_name = "osm_admin" # TODO: get project name from DB
233
234 # Render workflow
235 manifest = self.render_jinja_template(
236 workflow_template,
237 output_file=None,
238 workflow_name=workflow_name,
239 git_fleet_url=f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
240 git_sw_catalogs_url=f"{self._repo_base_url}/{self._repo_user}/sw-catalogs-osm.git",
241 cluster_name=cluster_name,
242 cluster_kustomization_name=cluster_kustomization_name,
243 osm_project_name=osm_project_name,
244 workflow_debug=self._workflow_debug,
245 workflow_dry_run=self._workflow_dry_run,
246 )
247 self.logger.info(manifest)
248
249 # Submit workflow
250 self._kubectl.create_generic_object(
251 namespace="osm-workflows",
252 manifest_dict=yaml.safe_load(manifest),
253 api_group="argoproj.io",
254 api_plural="workflows",
255 api_version="v1alpha1",
256 )
257 return workflow_name
258
259
260async def register_cluster(self, op_id, op_params, content):
garciadeblas1f71bab2024-10-22 14:04:36 +0200261 self.logger.info(f"register_cluster Enter. Operation {op_id}. Params: {op_params}")
262 # self.logger.debug(f"Content: {content}")
garciadeblas96b94f52024-07-08 16:18:21 +0200263
264 db_cluster = content["cluster"]
garciadeblas96b94f52024-07-08 16:18:21 +0200265 cluster_name = db_cluster["git_name"].lower()
266
garciadeblasf1458032024-09-17 13:25:06 +0200267 workflow_template = "launcher-bootstrap-cluster.j2"
268 workflow_name = f"register-cluster-{db_cluster['_id']}"
269
270 # Get age key
271 public_key_new_cluster, private_key_new_cluster = gather_age_key(db_cluster)
272 self.logger.debug(f"public_key_new_cluster={public_key_new_cluster}")
273 self.logger.debug(f"private_key_new_cluster={private_key_new_cluster}")
274
275 # Create temporal secret with agekey
276 secret_name = f"secret-age-{cluster_name}"
277 secret_namespace = "osm-workflows"
278 secret_key = "agekey"
279 secret_value = private_key_new_cluster
garciadeblas96b94f52024-07-08 16:18:21 +0200280 await self.create_secret(
281 secret_name,
282 secret_namespace,
283 secret_key,
284 secret_value,
285 )
286
garciadeblasf1458032024-09-17 13:25:06 +0200287 # Create secret with kubeconfig
288 secret_name2 = f"kubeconfig-{cluster_name}"
289 secret_namespace2 = "managed-resources"
290 secret_key2 = "kubeconfig"
291 secret_value2 = yaml.safe_dump(
292 db_cluster["credentials"], indent=4, default_flow_style=False, sort_keys=False
293 )
294 await self.create_secret(
295 secret_name2,
296 secret_namespace2,
297 secret_key2,
298 secret_value2,
299 )
300
301 # Additional params for the workflow
302 cluster_kustomization_name = cluster_name
303 osm_project_name = "osm_admin" # TODO: get project name from content
garciadeblasf1458032024-09-17 13:25:06 +0200304
305 manifest = self.render_jinja_template(
306 workflow_template,
307 output_file=None,
308 workflow_name=workflow_name,
309 git_fleet_url=f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
310 git_sw_catalogs_url=f"{self._repo_base_url}/{self._repo_user}/sw-catalogs-osm.git",
311 cluster_name=cluster_name,
312 cluster_kustomization_name=cluster_kustomization_name,
garciadeblasf1458032024-09-17 13:25:06 +0200313 public_key_mgmt=self._pubkey,
314 public_key_new_cluster=public_key_new_cluster,
315 secret_name_private_key_new_cluster=secret_name,
316 osm_project_name=osm_project_name,
317 workflow_debug=self._workflow_debug,
318 workflow_dry_run=self._workflow_dry_run,
319 )
320 self.logger.debug(f"Workflow manifest: {manifest}")
321
322 # Submit workflow
323 self._kubectl.create_generic_object(
324 namespace="osm-workflows",
325 manifest_dict=yaml.safe_load(manifest),
326 api_group="argoproj.io",
327 api_plural="workflows",
328 api_version="v1alpha1",
329 )
garciadeblas96b94f52024-07-08 16:18:21 +0200330 return workflow_name
331
332
333async def deregister_cluster(self, op_id, op_params, content):
garciadeblas1f71bab2024-10-22 14:04:36 +0200334 self.logger.info(
335 f"deregister_cluster Enter. Operation {op_id}. Params: {op_params}"
336 )
337 # self.logger.debug(f"Content: {content}")
garciadeblasf1458032024-09-17 13:25:06 +0200338
339 db_cluster = content["cluster"]
340 cluster_name = db_cluster["git_name"].lower()
341
342 workflow_template = "launcher-disconnect-flux-remote-cluster.j2"
343 workflow_name = f"deregister-cluster-{db_cluster['_id']}"
344
345 # Additional params for the workflow
346 cluster_kustomization_name = cluster_name
347 osm_project_name = "osm_admin" # TODO: get project name from DB
348
349 # Render workflow
350 manifest = self.render_jinja_template(
351 workflow_template,
352 output_file=None,
353 workflow_name=workflow_name,
354 git_fleet_url=f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
355 cluster_kustomization_name=cluster_kustomization_name,
356 osm_project_name=osm_project_name,
357 workflow_debug=self._workflow_debug,
358 workflow_dry_run=self._workflow_dry_run,
359 )
360 self.logger.info(manifest)
361
362 # Submit workflow
363 self._kubectl.create_generic_object(
364 namespace="osm-workflows",
365 manifest_dict=yaml.safe_load(manifest),
366 api_group="argoproj.io",
367 api_plural="workflows",
368 api_version="v1alpha1",
369 )
garciadeblas96b94f52024-07-08 16:18:21 +0200370 return workflow_name
371
372
373async def get_cluster_credentials(self, db_cluster):
374 """
375 returns the kubeconfig file of a K8s cluster in a dictionary
376 """
garciadeblas1f71bab2024-10-22 14:04:36 +0200377 self.logger.info("get_cluster_credentials Enter")
378 # self.logger.debug(f"Content: {db_cluster}")
garciadeblas96b94f52024-07-08 16:18:21 +0200379
380 secret_name = f"kubeconfig-{db_cluster['git_name'].lower()}"
381 secret_namespace = "managed-resources"
382 secret_key = "kubeconfig"
383
384 self.logger.info(f"Checking content of secret {secret_name} ...")
385 try:
386 returned_secret_data = await self._kubectl.get_secret_content(
387 name=secret_name,
388 namespace=secret_namespace,
389 )
390 returned_secret_value = base64.b64decode(
391 returned_secret_data[secret_key]
392 ).decode("utf-8")
393 return True, yaml.safe_load(returned_secret_value)
394 except Exception as e:
395 message = f"Not possible to get the credentials of the cluster. Exception: {e}"
396 self.logger.critical(message)
397 return False, message
398
399
garciadeblas4d26e292024-09-16 12:53:07 +0200400async def clean_items_cluster_create(self, op_id, op_params, content):
garciadeblas1f71bab2024-10-22 14:04:36 +0200401 self.logger.info(
402 f"clean_items_cluster_create Enter. Operation {op_id}. Params: {op_params}"
403 )
404 self.logger.debug(f"Content: {content}")
garciadeblas4d26e292024-09-16 12:53:07 +0200405 items = {
406 "secrets": [
407 {
408 "name": f"secret-age-{content['cluster']['git_name'].lower()}",
409 "namespace": "osm-workflows",
410 }
411 ]
412 }
413 try:
414 await self.clean_items(items)
415 return True, "OK"
416 except Exception as e:
417 return False, f"Error while cleaning items: {e}"
418
419
420async def clean_items_cluster_update(self, op_id, op_params, content):
garciadeblas1f71bab2024-10-22 14:04:36 +0200421 self.logger.info(
422 f"clean_items_cluster_update Enter. Operation {op_id}. Params: {op_params}"
423 )
424 # self.logger.debug(f"Content: {content}")
garciadeblas4d26e292024-09-16 12:53:07 +0200425 return await self.clean_items_cluster_create(op_id, op_params, content)
426
427
garciadeblasf1458032024-09-17 13:25:06 +0200428async def clean_items_cluster_register(self, op_id, op_params, content):
garciadeblas1f71bab2024-10-22 14:04:36 +0200429 self.logger.info(
430 f"clean_items_cluster_register Enter. Operation {op_id}. Params: {op_params}"
431 )
432 # self.logger.debug(f"Content: {content}")
garciadeblasf1458032024-09-17 13:25:06 +0200433 # Clean secrets
434 cluster_name = content["cluster"]["git_name"].lower()
435 items = {
436 "secrets": [
437 {
438 "name": f"secret-age-{cluster_name}",
439 "namespace": "osm-workflows",
440 },
441 ]
442 }
443
444 try:
445 await self.clean_items(items)
garciadeblas105ec222024-10-15 13:14:39 +0200446 return True, "OK"
garciadeblasf1458032024-09-17 13:25:06 +0200447 except Exception as e:
448 return False, f"Error while cleaning items: {e}"
449
450
garciadeblas96b94f52024-07-08 16:18:21 +0200451async def check_create_cluster(self, op_id, op_params, content):
452 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
garciadeblas3bc3b5e2024-10-18 11:36:15 +0200453 db_cluster = content["cluster"]
454 cluster_name = db_cluster["git_name"].lower()
455 cluster_kustomization_name = cluster_name
456 db_vim_account = content["vim_account"]
457 cloud_type = db_vim_account["vim_type"]
garciadeblas8424d1b2024-10-24 12:52:11 +0200458 nodepool_name = ""
459 if cloud_type == "aws":
460 nodepool_name = f"{cluster_name}-nodegroup"
461 cluster_name = f"{cluster_name}-cluster"
462 elif cloud_type == "gcp":
463 nodepool_name = f"nodepool-{cluster_name}"
garciadeblas3bc3b5e2024-10-18 11:36:15 +0200464 bootstrap = op_params.get("bootstrap", True)
garciadeblas8424d1b2024-10-24 12:52:11 +0200465 if cloud_type in ("azure", "gcp", "aws"):
466 checkings_list = [
467 {
468 "item": "kustomization",
469 "name": cluster_kustomization_name,
470 "namespace": "managed-resources",
471 "flag": "Ready",
472 "timeout": self._odu_checkloop_kustomization_timeout,
473 "enable": True,
474 },
475 {
476 "item": f"cluster_{cloud_type}",
477 "name": cluster_name,
478 "namespace": "",
479 "flag": "Synced",
480 "timeout": self._odu_checkloop_resource_timeout,
481 "enable": True,
482 },
483 {
484 "item": f"cluster_{cloud_type}",
485 "name": cluster_name,
486 "namespace": "",
487 "flag": "Ready",
488 "timeout": self._odu_checkloop_resource_timeout,
489 "enable": True,
490 },
491 {
492 "item": "kustomization",
493 "name": f"{cluster_kustomization_name}-bstrp-fluxctrl",
494 "namespace": "managed-resources",
495 "flag": "Ready",
496 "timeout": self._odu_checkloop_kustomization_timeout,
497 "enable": bootstrap,
498 },
499 ]
500 else:
garciadeblas3bc3b5e2024-10-18 11:36:15 +0200501 return False, "Not suitable VIM account to check cluster status"
garciadeblas8424d1b2024-10-24 12:52:11 +0200502 if nodepool_name:
503 nodepool_check = {
504 "item": f"nodepool_{cloud_type}",
505 "name": nodepool_name,
garciadeblas9564ace2024-10-22 12:54:22 +0200506 "namespace": "",
507 "flag": "Ready",
508 "timeout": self._odu_checkloop_resource_timeout,
509 "enable": True,
garciadeblas8424d1b2024-10-24 12:52:11 +0200510 }
511 checkings_list.insert(3, nodepool_check)
garciadeblas9564ace2024-10-22 12:54:22 +0200512 return await self.common_check_list(checkings_list)
garciadeblas96b94f52024-07-08 16:18:21 +0200513
514
515async def check_update_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_delete_cluster(self, op_id, op_params, content):
521 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
522 return True, "OK"
523
524
525async def check_register_cluster(self, op_id, op_params, content):
526 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
527 return True, "OK"
528
529
530async def check_deregister_cluster(self, op_id, op_params, content):
garciadeblasf1458032024-09-17 13:25:06 +0200531 self.logger.info("check_deregister_cluster Enter")
garciadeblas96b94f52024-07-08 16:18:21 +0200532 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
garciadeblasf1458032024-09-17 13:25:06 +0200533 # Clean secrets
534 self.logger.info("Cleaning kubeconfig")
535 cluster_name = content["cluster"]["git_name"].lower()
536 items = {
537 "secrets": [
538 {
539 "name": f"kubeconfig-{cluster_name}",
540 "namespace": "managed-resources",
541 },
542 ]
543 }
544
545 try:
546 await self.clean_items(items)
547 except Exception as e:
548 return False, f"Error while cleaning items: {e}"
garciadeblas96b94f52024-07-08 16:18:21 +0200549 return True, "OK"