blob: 719c7a8a673f900922c727c56c70c8f142599f61 [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
garciadeblas96b94f52024-07-08 16:18:21 +0200139
140async def update_cluster(self, op_id, op_params, content):
141 self.logger.info("Update cluster eks workflow Enter")
142 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
143
144 db_cluster = content["cluster"]
145 db_vim_account = content["vim_account"]
146
147 workflow_template = "launcher-update-crossplane-cluster.j2"
148 workflow_name = f"delete-cluster-{db_cluster['_id']}"
149 # cluster_name = db_cluster["name"].lower()
150 cluster_name = db_cluster["git_name"].lower()
151
152 # Get age key
153 public_key_cluster, private_key_cluster = gather_age_key(db_cluster)
154 self.logger.debug(f"public_key_new_cluster={public_key_cluster}")
155 self.logger.debug(f"private_key_new_cluster={private_key_cluster}")
156
157 # Create secret with agekey
158 secret_name = f"secret-age-{cluster_name}"
159 secret_namespace = "osm-workflows"
160 secret_key = "agekey"
161 secret_value = private_key_cluster
162 await self.create_secret(
163 secret_name,
164 secret_namespace,
165 secret_key,
166 secret_value,
167 )
168
169 # Additional params for the workflow
170 cluster_kustomization_name = cluster_name
171 osm_project_name = "osm_admin" # TODO: get project name from db_cluster
172 vim_account_id = db_cluster["vim_account"]
173 providerconfig_name = f"{vim_account_id}-config"
174 vim_type = db_vim_account["vim_type"]
175 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,
198 vm_size=db_cluster["node_size"],
199 node_count=db_cluster["node_count"],
200 k8s_version=db_cluster["k8s_version"],
201 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):
220 self.logger.info("Delete cluster workflow Enter")
221 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
222
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):
261 self.logger.info("Register cluster workflow Enter")
262 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
263
264 db_cluster = content["cluster"]
265 # cluster_name = db_cluster["name"].lower()
266 cluster_name = db_cluster["git_name"].lower()
267
268 # Create secret with kubeconfig
269 secret_name = f"kubeconfig-{cluster_name}"
270 secret_namespace = "managed-resources"
271 secret_key = "kubeconfig"
272 secret_value = yaml.safe_dump(
273 db_cluster["credentials"], indent=4, default_flow_style=False, sort_keys=False
274 )
275 await self.create_secret(
276 secret_name,
277 secret_namespace,
278 secret_key,
279 secret_value,
280 )
281
282 workflow_name = await self.create_cluster(op_id, op_params, content, True)
283 return workflow_name
284
285
286async def deregister_cluster(self, op_id, op_params, content):
287 self.logger.info("Deregister cluster workflow Enter")
288 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
289 workflow_name = await self.delete_cluster(op_id, op_params, content)
290 return workflow_name
291
292
293async def get_cluster_credentials(self, db_cluster):
294 """
295 returns the kubeconfig file of a K8s cluster in a dictionary
296 """
297 self.logger.info("Get cluster credentials Enter")
298 self.logger.info(f"Content: {db_cluster}")
299
300 secret_name = f"kubeconfig-{db_cluster['git_name'].lower()}"
301 secret_namespace = "managed-resources"
302 secret_key = "kubeconfig"
303
304 self.logger.info(f"Checking content of secret {secret_name} ...")
305 try:
306 returned_secret_data = await self._kubectl.get_secret_content(
307 name=secret_name,
308 namespace=secret_namespace,
309 )
310 returned_secret_value = base64.b64decode(
311 returned_secret_data[secret_key]
312 ).decode("utf-8")
313 return True, yaml.safe_load(returned_secret_value)
314 except Exception as e:
315 message = f"Not possible to get the credentials of the cluster. Exception: {e}"
316 self.logger.critical(message)
317 return False, message
318
319
garciadeblas4d26e292024-09-16 12:53:07 +0200320async def clean_items_cluster_create(self, op_id, op_params, content):
321 self.logger.info("Clean items cluster_create Enter")
322 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
323 items = {
324 "secrets": [
325 {
326 "name": f"secret-age-{content['cluster']['git_name'].lower()}",
327 "namespace": "osm-workflows",
328 }
329 ]
330 }
331 try:
332 await self.clean_items(items)
333 return True, "OK"
334 except Exception as e:
335 return False, f"Error while cleaning items: {e}"
336
337
338async def clean_items_cluster_update(self, op_id, op_params, content):
339 self.logger.info("Clean items cluster_update Enter")
340 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
341 return await self.clean_items_cluster_create(op_id, op_params, content)
342
343
garciadeblas96b94f52024-07-08 16:18:21 +0200344async def check_create_cluster(self, op_id, op_params, content):
345 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
346 return True, "OK"
347
348
349async def check_update_cluster(self, op_id, op_params, content):
350 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
351 return True, "OK"
352
353
354async def check_delete_cluster(self, op_id, op_params, content):
355 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
356 return True, "OK"
357
358
359async def check_register_cluster(self, op_id, op_params, content):
360 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
361 return True, "OK"
362
363
364async def check_deregister_cluster(self, op_id, op_params, content):
365 self.logger.info(f"Operation {op_id}. Params: {op_params}. Content: {content}")
366 return True, "OK"