blob: 8caed34e2a995d561204c6b4fac22c43051c3ac8 [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
41async def create_cluster(self, op_id, op_params, content, bootstrap_only=False):
42 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
48 # workflow_template = "launcher-create-aks-cluster-and-bootstrap.j2"
49 workflow_template = "launcher-create-crossplane-cluster-and-bootstrap.j2"
50 workflow_name = f"create-cluster-{db_cluster['_id']}"
51 # cluster_name = db_cluster["name"].lower()
52 cluster_name = db_cluster["git_name"].lower()
53
54 # Generate age key
55 # public_key_new_cluster, private_key_new_cluster = generate_age_key()
56 # Get age key
57 public_key_new_cluster, private_key_new_cluster = gather_age_key(db_cluster)
58 self.logger.debug(f"public_key_new_cluster={public_key_new_cluster}")
59 self.logger.debug(f"private_key_new_cluster={private_key_new_cluster}")
60
61 # Test kubectl connection
62 self.logger.debug(self._kubectl._get_kubectl_version())
63
64 # Create secret with agekey
65 secret_name = f"secret-age-{cluster_name}"
66 secret_namespace = "osm-workflows"
67 secret_key = "agekey"
68 secret_value = private_key_new_cluster
69 await self.create_secret(
70 secret_name,
71 secret_namespace,
72 secret_key,
73 secret_value,
74 )
75
76 # Additional params for the workflow
77 cluster_kustomization_name = cluster_name
78 osm_project_name = "osm_admin" # TODO: get project name from content
79 if bootstrap_only:
80 cluster_type = ""
81 providerconfig_name = ""
82 else:
83 vim_account_id = db_cluster["vim_account"]
84 providerconfig_name = f"{vim_account_id}-config"
85 vim_type = db_vim_account["vim_type"]
86 if vim_type == "azure":
87 cluster_type = "aks"
88 elif vim_type == "aws":
89 cluster_type = "eks"
90 elif vim_type == "gcp":
91 cluster_type = "gke"
92 else:
93 raise Exception("Not suitable VIM account to register cluster")
94
95 # Render workflow
96 # workflow_kwargs = {
97 # "git_fleet_url": f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
98 # "git_sw_catalogs_url": f"{self._repo_base_url}/{self._repo_user}/sw-catalogs-osm.git",
99 # }
100 # manifest = self.render_jinja_template(
101 # workflow_template,
102 # output_file=None,
103 # **workflow_kwargs
104 # )
105 manifest = self.render_jinja_template(
106 workflow_template,
107 output_file=None,
108 workflow_name=workflow_name,
109 git_fleet_url=f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
110 git_sw_catalogs_url=f"{self._repo_base_url}/{self._repo_user}/sw-catalogs-osm.git",
111 cluster_name=cluster_name,
112 cluster_type=cluster_type,
113 cluster_kustomization_name=cluster_kustomization_name,
114 providerconfig_name=providerconfig_name,
115 public_key_mgmt=self._pubkey,
116 public_key_new_cluster=public_key_new_cluster,
117 secret_name_private_key_new_cluster=secret_name,
118 vm_size=db_cluster["node_size"],
119 node_count=db_cluster["node_count"],
120 k8s_version=db_cluster["k8s_version"],
121 cluster_location=db_cluster["region_name"],
122 osm_project_name=osm_project_name,
123 rg_name=db_cluster["resource_group"],
124 workflow_debug=self._workflow_debug,
125 workflow_dry_run=self._workflow_dry_run,
126 )
127 self.logger.debug(f"Workflow manifest: {manifest}")
128
129 # Submit workflow
130 self._kubectl.create_generic_object(
131 namespace="osm-workflows",
132 manifest_dict=yaml.safe_load(manifest),
133 api_group="argoproj.io",
134 api_plural="workflows",
135 api_version="v1alpha1",
136 )
137 return workflow_name
138
139 # self.logger.info(f"Deleting secret {secret_name} in namespace {secret_namespace} ...")
140 # self._kubectl.delete_secret(name=secret_name, namespace=secret_namespace)
141 # self.logger.info("DONE")
142
143 # self.logger.info(f"Listing secrets in namespace {secret_namespace} ...")
144 # secret_list = self._kubectl.get_secrets(secret_namespace)
145 # # print(secret_list)
146 # for item in secret_list:
147 # print(item.metadata.name)
148 # self.logger.info("DONE")
149
150 # self.logger.info(f"Deleting secrets in namespace {secret_namespace} ...")
151 # for item in secret_list:
152 # print(f"Deleting {item.metadata.name} ...")
153 # self._kubectl.delete_secret(
154 # name=item.metadata.name,
155 # namespace=secret_namespace,
156 # )
157 # self.logger.info("DELETED")
158 # self.logger.info("DONE")
159
160
161async def update_cluster(self, op_id, op_params, content):
162 self.logger.info("Update cluster eks workflow Enter")
163 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
164
165 db_cluster = content["cluster"]
166 db_vim_account = content["vim_account"]
167
168 workflow_template = "launcher-update-crossplane-cluster.j2"
169 workflow_name = f"delete-cluster-{db_cluster['_id']}"
170 # cluster_name = db_cluster["name"].lower()
171 cluster_name = db_cluster["git_name"].lower()
172
173 # Get age key
174 public_key_cluster, private_key_cluster = gather_age_key(db_cluster)
175 self.logger.debug(f"public_key_new_cluster={public_key_cluster}")
176 self.logger.debug(f"private_key_new_cluster={private_key_cluster}")
177
178 # Create secret with agekey
179 secret_name = f"secret-age-{cluster_name}"
180 secret_namespace = "osm-workflows"
181 secret_key = "agekey"
182 secret_value = private_key_cluster
183 await self.create_secret(
184 secret_name,
185 secret_namespace,
186 secret_key,
187 secret_value,
188 )
189
190 # Additional params for the workflow
191 cluster_kustomization_name = cluster_name
192 osm_project_name = "osm_admin" # TODO: get project name from db_cluster
193 vim_account_id = db_cluster["vim_account"]
194 providerconfig_name = f"{vim_account_id}-config"
195 vim_type = db_vim_account["vim_type"]
196 if vim_type == "azure":
197 cluster_type = "aks"
198 elif vim_type == "aws":
199 cluster_type = "eks"
200 elif vim_type == "gcp":
201 cluster_type = "gke"
202 else:
203 raise Exception("Not suitable VIM account to update cluster")
204
205 # Render workflow
206 manifest = self.render_jinja_template(
207 workflow_template,
208 output_file=None,
209 workflow_name=workflow_name,
210 git_fleet_url=f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
211 git_sw_catalogs_url=f"{self._repo_base_url}/{self._repo_user}/sw-catalogs-osm.git",
212 cluster_name=cluster_name,
213 cluster_type=cluster_type,
214 cluster_kustomization_name=cluster_kustomization_name,
215 providerconfig_name=providerconfig_name,
216 public_key_mgmt=self._pubkey,
217 public_key_new_cluster=public_key_cluster,
218 secret_name_private_key_new_cluster=secret_name,
219 vm_size=db_cluster["node_size"],
220 node_count=db_cluster["node_count"],
221 k8s_version=db_cluster["k8s_version"],
222 cluster_location=db_cluster["region_name"],
223 osm_project_name=osm_project_name,
224 workflow_debug=self._workflow_debug,
225 workflow_dry_run=self._workflow_dry_run,
226 )
227 self.logger.info(manifest)
228
229 # Submit workflow
230 self._kubectl.create_generic_object(
231 namespace="osm-workflows",
232 manifest_dict=yaml.safe_load(manifest),
233 api_group="argoproj.io",
234 api_plural="workflows",
235 api_version="v1alpha1",
236 )
237 return workflow_name
238
239
240async def delete_cluster(self, op_id, op_params, content):
241 self.logger.info("Delete cluster workflow Enter")
242 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
243
244 db_cluster = content["cluster"]
245
246 workflow_template = "launcher-delete-cluster.j2"
247 workflow_name = f"delete-cluster-{db_cluster['_id']}"
248 # cluster_name = db_cluster["name"].lower()
249 cluster_name = db_cluster["git_name"].lower()
250
251 # Additional params for the workflow
252 cluster_kustomization_name = cluster_name
253 osm_project_name = "osm_admin" # TODO: get project name from DB
254
255 # Render workflow
256 manifest = self.render_jinja_template(
257 workflow_template,
258 output_file=None,
259 workflow_name=workflow_name,
260 git_fleet_url=f"{self._repo_base_url}/{self._repo_user}/fleet-osm.git",
261 git_sw_catalogs_url=f"{self._repo_base_url}/{self._repo_user}/sw-catalogs-osm.git",
262 cluster_name=cluster_name,
263 cluster_kustomization_name=cluster_kustomization_name,
264 osm_project_name=osm_project_name,
265 workflow_debug=self._workflow_debug,
266 workflow_dry_run=self._workflow_dry_run,
267 )
268 self.logger.info(manifest)
269
270 # Submit workflow
271 self._kubectl.create_generic_object(
272 namespace="osm-workflows",
273 manifest_dict=yaml.safe_load(manifest),
274 api_group="argoproj.io",
275 api_plural="workflows",
276 api_version="v1alpha1",
277 )
278 return workflow_name
279
280
281async def register_cluster(self, op_id, op_params, content):
282 self.logger.info("Register cluster workflow Enter")
283 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
284
285 db_cluster = content["cluster"]
286 # cluster_name = db_cluster["name"].lower()
287 cluster_name = db_cluster["git_name"].lower()
288
289 # Create secret with kubeconfig
290 secret_name = f"kubeconfig-{cluster_name}"
291 secret_namespace = "managed-resources"
292 secret_key = "kubeconfig"
293 secret_value = yaml.safe_dump(
294 db_cluster["credentials"], indent=4, default_flow_style=False, sort_keys=False
295 )
296 await self.create_secret(
297 secret_name,
298 secret_namespace,
299 secret_key,
300 secret_value,
301 )
302
303 workflow_name = await self.create_cluster(op_id, op_params, content, True)
304 return workflow_name
305
306
307async def deregister_cluster(self, op_id, op_params, content):
308 self.logger.info("Deregister cluster workflow Enter")
309 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
310 workflow_name = await self.delete_cluster(op_id, op_params, content)
311 return workflow_name
312
313
314async def get_cluster_credentials(self, db_cluster):
315 """
316 returns the kubeconfig file of a K8s cluster in a dictionary
317 """
318 self.logger.info("Get cluster credentials Enter")
319 self.logger.info(f"Content: {db_cluster}")
320
321 secret_name = f"kubeconfig-{db_cluster['git_name'].lower()}"
322 secret_namespace = "managed-resources"
323 secret_key = "kubeconfig"
324
325 self.logger.info(f"Checking content of secret {secret_name} ...")
326 try:
327 returned_secret_data = await self._kubectl.get_secret_content(
328 name=secret_name,
329 namespace=secret_namespace,
330 )
331 returned_secret_value = base64.b64decode(
332 returned_secret_data[secret_key]
333 ).decode("utf-8")
334 return True, yaml.safe_load(returned_secret_value)
335 except Exception as e:
336 message = f"Not possible to get the credentials of the cluster. Exception: {e}"
337 self.logger.critical(message)
338 return False, message
339
340
341async def check_create_cluster(self, op_id, op_params, content):
342 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
343 return True, "OK"
344
345
346async def check_update_cluster(self, op_id, op_params, content):
347 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
348 return True, "OK"
349
350
351async def check_delete_cluster(self, op_id, op_params, content):
352 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
353 return True, "OK"
354
355
356async def check_register_cluster(self, op_id, op_params, content):
357 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
358 return True, "OK"
359
360
361async def check_deregister_cluster(self, op_id, op_params, content):
362 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
363 return True, "OK"