blob: 935cc02c9a1817e4cfec4300a5b1f77bba3b77d2 [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"]
138
139 workflow_template = "launcher-update-crossplane-cluster.j2"
140 workflow_name = f"delete-cluster-{db_cluster['_id']}"
141 # cluster_name = db_cluster["name"].lower()
142 cluster_name = db_cluster["git_name"].lower()
143
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"]
167 if vim_type == "azure":
168 cluster_type = "aks"
169 elif vim_type == "aws":
170 cluster_type = "eks"
171 elif vim_type == "gcp":
172 cluster_type = "gke"
173 else:
174 raise Exception("Not suitable VIM account to update cluster")
175
176 # Render workflow
177 manifest = self.render_jinja_template(
178 workflow_template,
179 output_file=None,
180 workflow_name=workflow_name,
181 git_fleet_url=f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
182 git_sw_catalogs_url=f"{self._repo_base_url}/{self._repo_user}/sw-catalogs-osm.git",
183 cluster_name=cluster_name,
184 cluster_type=cluster_type,
185 cluster_kustomization_name=cluster_kustomization_name,
186 providerconfig_name=providerconfig_name,
187 public_key_mgmt=self._pubkey,
188 public_key_new_cluster=public_key_cluster,
189 secret_name_private_key_new_cluster=secret_name,
190 vm_size=db_cluster["node_size"],
191 node_count=db_cluster["node_count"],
192 k8s_version=db_cluster["k8s_version"],
193 cluster_location=db_cluster["region_name"],
194 osm_project_name=osm_project_name,
195 workflow_debug=self._workflow_debug,
196 workflow_dry_run=self._workflow_dry_run,
197 )
198 self.logger.info(manifest)
199
200 # Submit workflow
201 self._kubectl.create_generic_object(
202 namespace="osm-workflows",
203 manifest_dict=yaml.safe_load(manifest),
204 api_group="argoproj.io",
205 api_plural="workflows",
206 api_version="v1alpha1",
207 )
208 return workflow_name
209
210
211async def delete_cluster(self, op_id, op_params, content):
212 self.logger.info("Delete cluster workflow Enter")
213 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
214
215 db_cluster = content["cluster"]
216
217 workflow_template = "launcher-delete-cluster.j2"
218 workflow_name = f"delete-cluster-{db_cluster['_id']}"
219 # cluster_name = db_cluster["name"].lower()
220 cluster_name = db_cluster["git_name"].lower()
221
222 # Additional params for the workflow
223 cluster_kustomization_name = cluster_name
224 osm_project_name = "osm_admin" # TODO: get project name from DB
225
226 # Render workflow
227 manifest = self.render_jinja_template(
228 workflow_template,
229 output_file=None,
230 workflow_name=workflow_name,
231 git_fleet_url=f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
232 git_sw_catalogs_url=f"{self._repo_base_url}/{self._repo_user}/sw-catalogs-osm.git",
233 cluster_name=cluster_name,
234 cluster_kustomization_name=cluster_kustomization_name,
235 osm_project_name=osm_project_name,
236 workflow_debug=self._workflow_debug,
237 workflow_dry_run=self._workflow_dry_run,
238 )
239 self.logger.info(manifest)
240
241 # Submit workflow
242 self._kubectl.create_generic_object(
243 namespace="osm-workflows",
244 manifest_dict=yaml.safe_load(manifest),
245 api_group="argoproj.io",
246 api_plural="workflows",
247 api_version="v1alpha1",
248 )
249 return workflow_name
250
251
252async def register_cluster(self, op_id, op_params, content):
253 self.logger.info("Register cluster workflow Enter")
254 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
255
256 db_cluster = content["cluster"]
garciadeblas96b94f52024-07-08 16:18:21 +0200257 cluster_name = db_cluster["git_name"].lower()
258
garciadeblasdde3a312024-09-17 13:25:06 +0200259 workflow_template = "launcher-bootstrap-cluster.j2"
260 workflow_name = f"register-cluster-{db_cluster['_id']}"
261
262 # Get age key
263 public_key_new_cluster, private_key_new_cluster = gather_age_key(db_cluster)
264 self.logger.debug(f"public_key_new_cluster={public_key_new_cluster}")
265 self.logger.debug(f"private_key_new_cluster={private_key_new_cluster}")
266
267 # Create temporal secret with agekey
268 secret_name = f"secret-age-{cluster_name}"
269 secret_namespace = "osm-workflows"
270 secret_key = "agekey"
271 secret_value = private_key_new_cluster
garciadeblas96b94f52024-07-08 16:18:21 +0200272 await self.create_secret(
273 secret_name,
274 secret_namespace,
275 secret_key,
276 secret_value,
277 )
278
garciadeblasdde3a312024-09-17 13:25:06 +0200279 # Create secret with kubeconfig
280 secret_name2 = f"kubeconfig-{cluster_name}"
281 secret_namespace2 = "managed-resources"
282 secret_key2 = "kubeconfig"
283 secret_value2 = yaml.safe_dump(
284 db_cluster["credentials"], indent=4, default_flow_style=False, sort_keys=False
285 )
286 await self.create_secret(
287 secret_name2,
288 secret_namespace2,
289 secret_key2,
290 secret_value2,
291 )
292
293 # Additional params for the workflow
294 cluster_kustomization_name = cluster_name
295 osm_project_name = "osm_admin" # TODO: get project name from content
296 vim_account_id = db_cluster["vim_account"]
297 providerconfig_name = f"{vim_account_id}-config"
298
299 manifest = self.render_jinja_template(
300 workflow_template,
301 output_file=None,
302 workflow_name=workflow_name,
303 git_fleet_url=f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
304 git_sw_catalogs_url=f"{self._repo_base_url}/{self._repo_user}/sw-catalogs-osm.git",
305 cluster_name=cluster_name,
306 cluster_kustomization_name=cluster_kustomization_name,
307 providerconfig_name=providerconfig_name,
308 public_key_mgmt=self._pubkey,
309 public_key_new_cluster=public_key_new_cluster,
310 secret_name_private_key_new_cluster=secret_name,
311 osm_project_name=osm_project_name,
312 workflow_debug=self._workflow_debug,
313 workflow_dry_run=self._workflow_dry_run,
314 )
315 self.logger.debug(f"Workflow manifest: {manifest}")
316
317 # Submit workflow
318 self._kubectl.create_generic_object(
319 namespace="osm-workflows",
320 manifest_dict=yaml.safe_load(manifest),
321 api_group="argoproj.io",
322 api_plural="workflows",
323 api_version="v1alpha1",
324 )
garciadeblas96b94f52024-07-08 16:18:21 +0200325 return workflow_name
326
327
328async def deregister_cluster(self, op_id, op_params, content):
329 self.logger.info("Deregister cluster workflow Enter")
330 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
garciadeblasdde3a312024-09-17 13:25:06 +0200331
332 db_cluster = content["cluster"]
333 cluster_name = db_cluster["git_name"].lower()
334
335 workflow_template = "launcher-disconnect-flux-remote-cluster.j2"
336 workflow_name = f"deregister-cluster-{db_cluster['_id']}"
337
338 # Additional params for the workflow
339 cluster_kustomization_name = cluster_name
340 osm_project_name = "osm_admin" # TODO: get project name from DB
341
342 # Render workflow
343 manifest = self.render_jinja_template(
344 workflow_template,
345 output_file=None,
346 workflow_name=workflow_name,
347 git_fleet_url=f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
348 cluster_kustomization_name=cluster_kustomization_name,
349 osm_project_name=osm_project_name,
350 workflow_debug=self._workflow_debug,
351 workflow_dry_run=self._workflow_dry_run,
352 )
353 self.logger.info(manifest)
354
355 # Submit workflow
356 self._kubectl.create_generic_object(
357 namespace="osm-workflows",
358 manifest_dict=yaml.safe_load(manifest),
359 api_group="argoproj.io",
360 api_plural="workflows",
361 api_version="v1alpha1",
362 )
garciadeblas96b94f52024-07-08 16:18:21 +0200363 return workflow_name
364
365
366async def get_cluster_credentials(self, db_cluster):
367 """
368 returns the kubeconfig file of a K8s cluster in a dictionary
369 """
370 self.logger.info("Get cluster credentials Enter")
371 self.logger.info(f"Content: {db_cluster}")
372
373 secret_name = f"kubeconfig-{db_cluster['git_name'].lower()}"
374 secret_namespace = "managed-resources"
375 secret_key = "kubeconfig"
376
377 self.logger.info(f"Checking content of secret {secret_name} ...")
378 try:
379 returned_secret_data = await self._kubectl.get_secret_content(
380 name=secret_name,
381 namespace=secret_namespace,
382 )
383 returned_secret_value = base64.b64decode(
384 returned_secret_data[secret_key]
385 ).decode("utf-8")
386 return True, yaml.safe_load(returned_secret_value)
387 except Exception as e:
388 message = f"Not possible to get the credentials of the cluster. Exception: {e}"
389 self.logger.critical(message)
390 return False, message
391
392
garciadeblas28bff0f2024-09-16 12:53:07 +0200393async def clean_items_cluster_create(self, op_id, op_params, content):
394 self.logger.info("Clean items cluster_create Enter")
395 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
396 items = {
397 "secrets": [
398 {
399 "name": f"secret-age-{content['cluster']['git_name'].lower()}",
400 "namespace": "osm-workflows",
401 }
402 ]
403 }
404 try:
405 await self.clean_items(items)
406 return True, "OK"
407 except Exception as e:
408 return False, f"Error while cleaning items: {e}"
409
410
411async def clean_items_cluster_update(self, op_id, op_params, content):
412 self.logger.info("Clean items cluster_update Enter")
413 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
414 return await self.clean_items_cluster_create(op_id, op_params, content)
415
416
garciadeblasdde3a312024-09-17 13:25:06 +0200417async def clean_items_cluster_register(self, op_id, op_params, content):
418 self.logger.info("Clean items cluster_register Enter")
419 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
420 # Clean secrets
421 cluster_name = content["cluster"]["git_name"].lower()
422 items = {
423 "secrets": [
424 {
425 "name": f"secret-age-{cluster_name}",
426 "namespace": "osm-workflows",
427 },
428 ]
429 }
430
431 try:
432 await self.clean_items(items)
433 except Exception as e:
434 return False, f"Error while cleaning items: {e}"
435
436
garciadeblas96b94f52024-07-08 16:18:21 +0200437async def check_create_cluster(self, op_id, op_params, content):
438 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
439 return True, "OK"
440
441
442async def check_update_cluster(self, op_id, op_params, content):
443 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
444 return True, "OK"
445
446
447async def check_delete_cluster(self, op_id, op_params, content):
448 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
449 return True, "OK"
450
451
452async def check_register_cluster(self, op_id, op_params, content):
453 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
454 return True, "OK"
455
456
457async def check_deregister_cluster(self, op_id, op_params, content):
garciadeblasdde3a312024-09-17 13:25:06 +0200458 self.logger.info("check_deregister_cluster Enter")
garciadeblas96b94f52024-07-08 16:18:21 +0200459 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
garciadeblasdde3a312024-09-17 13:25:06 +0200460 # Clean secrets
461 self.logger.info("Cleaning kubeconfig")
462 cluster_name = content["cluster"]["git_name"].lower()
463 items = {
464 "secrets": [
465 {
466 "name": f"kubeconfig-{cluster_name}",
467 "namespace": "managed-resources",
468 },
469 ]
470 }
471
472 try:
473 await self.clean_items(items)
474 except Exception as e:
475 return False, f"Error while cleaning items: {e}"
garciadeblas96b94f52024-07-08 16:18:21 +0200476 return True, "OK"