blob: 14f7fe07036b25498ec358bddff38e77a6131d52 [file] [log] [blame]
lloretgalleg1c83f2e2020-10-22 09:12:35 +00001##
2# Copyright 2019 Telefonica Investigacion y Desarrollo, S.A.U.
3# This file is part of OSM
4# All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15# implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18#
19# For those usages not covered by the Apache License, Version 2.0 please
20# contact with: nfvlabs@tid.es
21##
Pedro Escaleiraa8980cc2022-04-05 17:32:13 +010022from typing import Union
Daniel Arndtde6984b2023-06-27 16:42:41 -030023from shlex import quote
lloretgalleg1c83f2e2020-10-22 09:12:35 +000024import os
25import yaml
26
27from n2vc.k8s_helm_base_conn import K8sHelmBaseConnector
28from n2vc.exceptions import K8sException
29
30
31class K8sHelm3Connector(K8sHelmBaseConnector):
32
33 """
34 ####################################################################################
35 ################################### P U B L I C ####################################
36 ####################################################################################
37 """
38
39 def __init__(
garciadeblas82b591c2021-03-24 09:22:13 +010040 self,
41 fs: object,
42 db: object,
43 kubectl_command: str = "/usr/bin/kubectl",
44 helm_command: str = "/usr/bin/helm3",
45 log: object = None,
46 on_update_db=None,
lloretgalleg1c83f2e2020-10-22 09:12:35 +000047 ):
48 """
49 Initializes helm connector for helm v3
50
51 :param fs: file system for kubernetes and helm configuration
52 :param db: database object to write current operation status
53 :param kubectl_command: path to kubectl executable
54 :param helm_command: path to helm executable
55 :param log: logger
56 :param on_update_db: callback called when k8s connector updates database
57 """
58
59 # parent class
garciadeblas82b591c2021-03-24 09:22:13 +010060 K8sHelmBaseConnector.__init__(
61 self,
62 db=db,
63 log=log,
64 fs=fs,
65 kubectl_command=kubectl_command,
66 helm_command=helm_command,
67 on_update_db=on_update_db,
garciadeblas82b591c2021-03-24 09:22:13 +010068 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +000069
70 self.log.info("K8S Helm3 connector initialized")
71
lloretgalleg095392b2020-11-20 11:28:08 +000072 async def install(
garciadeblas82b591c2021-03-24 09:22:13 +010073 self,
74 cluster_uuid: str,
75 kdu_model: str,
76 kdu_instance: str,
77 atomic: bool = True,
78 timeout: float = 300,
79 params: dict = None,
80 db_dict: dict = None,
81 kdu_name: str = None,
82 namespace: str = None,
83 **kwargs,
lloretgalleg095392b2020-11-20 11:28:08 +000084 ):
David Garciaeb8943a2021-04-12 12:07:37 +020085 """Install a helm chart
86
87 :param cluster_uuid str: The UUID of the cluster to install to
garciadeblas04393192022-06-08 15:39:24 +020088 :param kdu_model str: chart/reference (string), which can be either
89 of these options:
90 - a name of chart available via the repos known by OSM
91 (e.g. stable/openldap, stable/openldap:1.2.4)
92 - a path to a packaged chart (e.g. mychart.tgz)
93 - a path to an unpacked chart directory or a URL (e.g. mychart)
David Garciaeb8943a2021-04-12 12:07:37 +020094 :param kdu_instance: Kdu instance name
95 :param atomic bool: If set, waits until the model is active and resets
96 the cluster on failure.
97 :param timeout int: The time, in seconds, to wait for the install
98 to finish
99 :param params dict: Key-value pairs of instantiation parameters
100 :param kdu_name: Name of the KDU instance to be installed
101 :param namespace: K8s namespace to use for the KDU instance
102
103 :param kwargs: Additional parameters (None yet)
104
105 :return: True if successful
106 """
Pedro Escaleirab41de172022-04-02 00:44:08 +0100107
108 self.log.debug("installing {} in cluster {}".format(kdu_model, cluster_uuid))
lloretgalleg095392b2020-11-20 11:28:08 +0000109
110 # sync local dir
Pedro Escaleirab41de172022-04-02 00:44:08 +0100111 self.fs.sync(from_path=cluster_uuid)
lloretgalleg095392b2020-11-20 11:28:08 +0000112
113 # init env, paths
114 paths, env = self._init_paths_env(
Pedro Escaleirab41de172022-04-02 00:44:08 +0100115 cluster_name=cluster_uuid, create_if_not_exist=True
lloretgalleg095392b2020-11-20 11:28:08 +0000116 )
117
118 # for helm3 if namespace does not exist must create it
119 if namespace and namespace != "kube-system":
Pedro Escaleirab41de172022-04-02 00:44:08 +0100120 if not await self._namespace_exists(cluster_uuid, namespace):
aktas2a3ffde2021-06-24 11:37:11 +0300121 try:
Gabriel Cuba5f069332023-04-25 19:26:19 -0500122 # TODO: refactor to use kubernetes API client
Pedro Escaleirab41de172022-04-02 00:44:08 +0100123 await self._create_namespace(cluster_uuid, namespace)
aktas2a3ffde2021-06-24 11:37:11 +0300124 except Exception as e:
Pedro Escaleirab41de172022-04-02 00:44:08 +0100125 if not await self._namespace_exists(cluster_uuid, namespace):
aktas2a3ffde2021-06-24 11:37:11 +0300126 err_msg = (
127 "namespace {} does not exist in cluster_id {} "
David Garcia4ae527e2021-07-26 16:04:59 +0200128 "error message: ".format(namespace, e)
aktas2a3ffde2021-06-24 11:37:11 +0300129 )
130 self.log.error(err_msg)
131 raise K8sException(err_msg)
lloretgalleg095392b2020-11-20 11:28:08 +0000132
David Garciac4da25c2021-02-23 11:47:29 +0100133 await self._install_impl(
Pedro Escaleirab41de172022-04-02 00:44:08 +0100134 cluster_uuid,
David Garciac4da25c2021-02-23 11:47:29 +0100135 kdu_model,
136 paths,
137 env,
138 kdu_instance,
139 atomic=atomic,
140 timeout=timeout,
141 params=params,
142 db_dict=db_dict,
143 kdu_name=kdu_name,
144 namespace=namespace,
145 )
lloretgalleg095392b2020-11-20 11:28:08 +0000146
147 # sync fs
Pedro Escaleirab41de172022-04-02 00:44:08 +0100148 self.fs.reverse_sync(from_path=cluster_uuid)
lloretgalleg095392b2020-11-20 11:28:08 +0000149
150 self.log.debug("Returning kdu_instance {}".format(kdu_instance))
David Garciac4da25c2021-02-23 11:47:29 +0100151 return True
lloretgalleg095392b2020-11-20 11:28:08 +0000152
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000153 async def inspect_kdu(self, kdu_model: str, repo_url: str = None) -> str:
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000154 self.log.debug(
155 "inspect kdu_model {} from (optional) repo: {}".format(kdu_model, repo_url)
156 )
157
aktas867418c2021-10-19 18:26:13 +0300158 return await self._exec_inspect_command(
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000159 inspect_command="all", kdu_model=kdu_model, repo_url=repo_url
160 )
161
162 """
163 ####################################################################################
164 ################################### P R I V A T E ##################################
165 ####################################################################################
166 """
167
168 def _init_paths_env(self, cluster_name: str, create_if_not_exist: bool = True):
169 """
170 Creates and returns base cluster and kube dirs and returns them.
171 Also created helm3 dirs according to new directory specification, paths are
172 returned and also environment variables that must be provided to execute commands
173
174 Helm 3 directory specification uses XDG categories for variable support:
175 - Cache: $XDG_CACHE_HOME, for example, ${HOME}/.cache/helm/
176 - Configuration: $XDG_CONFIG_HOME, for example, ${HOME}/.config/helm/
177 - Data: $XDG_DATA_HOME, for example ${HOME}/.local/share/helm
178
179 The variables assigned for this paths are:
180 (In the documentation the variables names are $HELM_PATH_CACHE, $HELM_PATH_CONFIG,
181 $HELM_PATH_DATA but looking and helm env the variable names are different)
182 - Cache: $HELM_CACHE_HOME
183 - Config: $HELM_CONFIG_HOME
184 - Data: $HELM_DATA_HOME
185 - helm kubeconfig: $KUBECONFIG
186
187 :param cluster_name: cluster_name
188 :return: Dictionary with config_paths and dictionary with helm environment variables
189 """
190
191 base = self.fs.path
192 if base.endswith("/") or base.endswith("\\"):
193 base = base[:-1]
194
195 # base dir for cluster
196 cluster_dir = base + "/" + cluster_name
197
198 # kube dir
199 kube_dir = cluster_dir + "/" + ".kube"
200 if create_if_not_exist and not os.path.exists(kube_dir):
201 self.log.debug("Creating dir {}".format(kube_dir))
202 os.makedirs(kube_dir)
203
204 helm_path_cache = cluster_dir + "/.cache/helm"
205 if create_if_not_exist and not os.path.exists(helm_path_cache):
206 self.log.debug("Creating dir {}".format(helm_path_cache))
207 os.makedirs(helm_path_cache)
208
209 helm_path_config = cluster_dir + "/.config/helm"
210 if create_if_not_exist and not os.path.exists(helm_path_config):
211 self.log.debug("Creating dir {}".format(helm_path_config))
212 os.makedirs(helm_path_config)
213
214 helm_path_data = cluster_dir + "/.local/share/helm"
215 if create_if_not_exist and not os.path.exists(helm_path_data):
216 self.log.debug("Creating dir {}".format(helm_path_data))
217 os.makedirs(helm_path_data)
218
219 config_filename = kube_dir + "/config"
220
221 # 2 - Prepare dictionary with paths
222 paths = {
223 "kube_dir": kube_dir,
224 "kube_config": config_filename,
garciadeblas82b591c2021-03-24 09:22:13 +0100225 "cluster_dir": cluster_dir,
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000226 }
227
228 # 3 - Prepare environment variables
229 env = {
230 "HELM_CACHE_HOME": helm_path_cache,
231 "HELM_CONFIG_HOME": helm_path_config,
232 "HELM_DATA_HOME": helm_path_data,
garciadeblas82b591c2021-03-24 09:22:13 +0100233 "KUBECONFIG": config_filename,
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000234 }
235
236 for file_name, file in paths.items():
237 if "dir" in file_name and not os.path.exists(file):
238 err_msg = "{} dir does not exist".format(file)
239 self.log.error(err_msg)
240 raise K8sException(err_msg)
241
242 return paths, env
243
aktas2a3ffde2021-06-24 11:37:11 +0300244 async def _namespace_exists(self, cluster_id, namespace) -> bool:
245 self.log.debug(
246 "checking if namespace {} exists cluster_id {}".format(
247 namespace, cluster_id
248 )
249 )
250 namespaces = await self._get_namespaces(cluster_id)
251 return namespace in namespaces if namespaces else False
252
garciadeblas82b591c2021-03-24 09:22:13 +0100253 async def _get_namespaces(self, cluster_id: str):
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000254 self.log.debug("get namespaces cluster_id {}".format(cluster_id))
255
256 # init config, env
257 paths, env = self._init_paths_env(
258 cluster_name=cluster_id, create_if_not_exist=True
259 )
260
261 command = "{} --kubeconfig={} get namespaces -o=yaml".format(
Daniel Arndtde6984b2023-06-27 16:42:41 -0300262 self.kubectl_command, quote(paths["kube_config"])
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000263 )
264 output, _rc = await self._local_async_exec(
265 command=command, raise_exception_on_error=True, env=env
266 )
267
268 data = yaml.load(output, Loader=yaml.SafeLoader)
269 namespaces = [item["metadata"]["name"] for item in data["items"]]
270 self.log.debug(f"namespaces {namespaces}")
271
272 return namespaces
273
garciadeblas82b591c2021-03-24 09:22:13 +0100274 async def _create_namespace(self, cluster_id: str, namespace: str):
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000275 self.log.debug(f"create namespace: {cluster_id} for cluster_id: {namespace}")
276
277 # init config, env
278 paths, env = self._init_paths_env(
279 cluster_name=cluster_id, create_if_not_exist=True
280 )
281
282 command = "{} --kubeconfig={} create namespace {}".format(
Daniel Arndtde6984b2023-06-27 16:42:41 -0300283 self.kubectl_command, quote(paths["kube_config"]), quote(namespace)
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000284 )
285 _, _rc = await self._local_async_exec(
286 command=command, raise_exception_on_error=True, env=env
287 )
288 self.log.debug(f"namespace {namespace} created")
289
290 return _rc
291
bravof7bd5c6a2021-11-17 11:14:57 -0300292 async def _get_services(
293 self, cluster_id: str, kdu_instance: str, namespace: str, kubeconfig: str
294 ):
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000295 # init config, env
296 paths, env = self._init_paths_env(
297 cluster_name=cluster_id, create_if_not_exist=True
298 )
299
bravof7bd5c6a2021-11-17 11:14:57 -0300300 command1 = "env KUBECONFIG={} {} get manifest {} --namespace={}".format(
Daniel Arndtde6984b2023-06-27 16:42:41 -0300301 kubeconfig, self._helm_command, quote(kdu_instance), quote(namespace)
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000302 )
Daniel Arndtde6984b2023-06-27 16:42:41 -0300303 command2 = "{} get --namespace={} -f -".format(
304 self.kubectl_command, quote(namespace)
305 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000306 output, _rc = await self._local_async_exec_pipe(
307 command1, command2, env=env, raise_exception_on_error=True
308 )
309 services = self._parse_services(output)
310
311 return services
312
313 async def _cluster_init(self, cluster_id, namespace, paths, env):
314 """
315 Implements the helm version dependent cluster initialization:
316 For helm3 it creates the namespace if it is not created
317 """
318 if namespace != "kube-system":
319 namespaces = await self._get_namespaces(cluster_id)
320 if namespace not in namespaces:
Gabriel Cuba5f069332023-04-25 19:26:19 -0500321 # TODO: refactor to use kubernetes API client
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000322 await self._create_namespace(cluster_id, namespace)
323
Pedro Escaleirab41de172022-04-02 00:44:08 +0100324 repo_list = await self.repo_list(cluster_id)
David Garcia4395cfa2021-05-28 16:21:51 +0200325 stable_repo = [repo for repo in repo_list if repo["name"] == "stable"]
326 if not stable_repo and self._stable_repo_url:
Pedro Escaleirab41de172022-04-02 00:44:08 +0100327 await self.repo_add(cluster_id, "stable", self._stable_repo_url)
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000328
329 # Returns False as no software needs to be uninstalled
330 return False
331
332 async def _uninstall_sw(self, cluster_id: str, namespace: str):
333 # nothing to do to uninstall sw
334 pass
335
336 async def _instances_list(self, cluster_id: str):
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000337 # init paths, env
338 paths, env = self._init_paths_env(
339 cluster_name=cluster_id, create_if_not_exist=True
340 )
341
garciadeblas82b591c2021-03-24 09:22:13 +0100342 command = "{} list --all-namespaces --output yaml".format(self._helm_command)
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000343 output, _rc = await self._local_async_exec(
344 command=command, raise_exception_on_error=True, env=env
345 )
346
347 if output and len(output) > 0:
348 self.log.debug("instances list output: {}".format(output))
349 return yaml.load(output, Loader=yaml.SafeLoader)
350 else:
351 return []
352
garciadeblas82b591c2021-03-24 09:22:13 +0100353 def _get_inspect_command(
Pedro Escaleira0fcb6fe2022-06-04 19:14:11 +0100354 self, show_command: str, kdu_model: str, repo_str: str, version: str
garciadeblas82b591c2021-03-24 09:22:13 +0100355 ):
Pedro Escaleira547f8232022-06-03 19:48:46 +0100356 """Generates the command to obtain the information about an Helm Chart package
357 (´helm show ...´ command)
358
359 Args:
360 show_command: the second part of the command (`helm show <show_command>`)
Gabriel Cuba1c1a2562023-11-20 01:08:39 -0500361 kdu_model: The name or path of a Helm Chart
362 repo_str: Helm Chart repository url
Pedro Escaleira547f8232022-06-03 19:48:46 +0100363 version: constraint with specific version of the Chart to use
364
365 Returns:
366 str: the generated Helm Chart command
367 """
368
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000369 inspect_command = "{} show {} {}{} {}".format(
Daniel Arndtde6984b2023-06-27 16:42:41 -0300370 self._helm_command, show_command, quote(kdu_model), repo_str, version
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000371 )
372 return inspect_command
373
aktas867418c2021-10-19 18:26:13 +0300374 def _get_get_command(
375 self, get_command: str, kdu_instance: str, namespace: str, kubeconfig: str
376 ):
377 get_command = (
378 "env KUBECONFIG={} {} get {} {} --namespace={} --output yaml".format(
Daniel Arndtde6984b2023-06-27 16:42:41 -0300379 kubeconfig,
380 self._helm_command,
381 get_command,
382 quote(kdu_instance),
383 quote(namespace),
aktas867418c2021-10-19 18:26:13 +0300384 )
385 )
386 return get_command
387
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000388 async def _status_kdu(
389 self,
390 cluster_id: str,
391 kdu_instance: str,
392 namespace: str = None,
Pedro Escaleiraa8980cc2022-04-05 17:32:13 +0100393 yaml_format: bool = False,
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000394 show_error_log: bool = False,
Pedro Escaleiraa8980cc2022-04-05 17:32:13 +0100395 ) -> Union[str, dict]:
garciadeblas82b591c2021-03-24 09:22:13 +0100396 self.log.debug(
397 "status of kdu_instance: {}, namespace: {} ".format(kdu_instance, namespace)
398 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000399
400 if not namespace:
401 namespace = "kube-system"
402
403 # init config, env
404 paths, env = self._init_paths_env(
405 cluster_name=cluster_id, create_if_not_exist=True
406 )
bravof7bd5c6a2021-11-17 11:14:57 -0300407 command = "env KUBECONFIG={} {} status {} --namespace={} --output yaml".format(
Daniel Arndtde6984b2023-06-27 16:42:41 -0300408 paths["kube_config"],
409 self._helm_command,
410 quote(kdu_instance),
411 quote(namespace),
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000412 )
413
414 output, rc = await self._local_async_exec(
415 command=command,
416 raise_exception_on_error=True,
417 show_error_log=show_error_log,
garciadeblas82b591c2021-03-24 09:22:13 +0100418 env=env,
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000419 )
420
Pedro Escaleiraa8980cc2022-04-05 17:32:13 +0100421 if yaml_format:
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000422 return str(output)
423
424 if rc != 0:
425 return None
426
427 data = yaml.load(output, Loader=yaml.SafeLoader)
428
429 # remove field 'notes' and manifest
430 try:
431 del data.get("info")["notes"]
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000432 except KeyError:
433 pass
434
Pedro Escaleiraed0ff052022-04-03 13:51:46 +0100435 # parse the manifest to a list of dictionaries
436 if "manifest" in data:
437 manifest_str = data.get("manifest")
438 manifest_docs = yaml.load_all(manifest_str, Loader=yaml.SafeLoader)
439
440 data["manifest"] = []
441 for doc in manifest_docs:
442 data["manifest"].append(doc)
443
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000444 return data
445
garciadeblas82b591c2021-03-24 09:22:13 +0100446 def _get_install_command(
447 self,
448 kdu_model: str,
449 kdu_instance: str,
450 namespace: str,
451 params_str: str,
452 version: str,
453 atomic: bool,
454 timeout: float,
bravof7bd5c6a2021-11-17 11:14:57 -0300455 kubeconfig: str,
garciadeblas82b591c2021-03-24 09:22:13 +0100456 ) -> str:
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000457 timeout_str = ""
458 if timeout:
459 timeout_str = "--timeout {}s".format(timeout)
460
461 # atomic
462 atomic_str = ""
463 if atomic:
464 atomic_str = "--atomic"
465 # namespace
466 namespace_str = ""
467 if namespace:
Daniel Arndtde6984b2023-06-27 16:42:41 -0300468 namespace_str = "--namespace {}".format(quote(namespace))
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000469
470 # version
471 version_str = ""
472 if version:
473 version_str = "--version {}".format(version)
474
475 command = (
bravof7bd5c6a2021-11-17 11:14:57 -0300476 "env KUBECONFIG={kubeconfig} {helm} install {name} {atomic} --output yaml "
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000477 "{params} {timeout} {ns} {model} {ver}".format(
bravof7bd5c6a2021-11-17 11:14:57 -0300478 kubeconfig=kubeconfig,
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000479 helm=self._helm_command,
Daniel Arndtde6984b2023-06-27 16:42:41 -0300480 name=quote(kdu_instance),
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000481 atomic=atomic_str,
482 params=params_str,
483 timeout=timeout_str,
484 ns=namespace_str,
Daniel Arndtde6984b2023-06-27 16:42:41 -0300485 model=quote(kdu_model),
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000486 ver=version_str,
487 )
488 )
489 return command
490
aktas867418c2021-10-19 18:26:13 +0300491 def _get_upgrade_scale_command(
492 self,
493 kdu_model: str,
494 kdu_instance: str,
495 namespace: str,
496 scale: int,
497 version: str,
498 atomic: bool,
499 replica_str: str,
500 timeout: float,
501 resource_name: str,
502 kubeconfig: str,
503 ) -> str:
Pedro Escaleira0a2060c2022-07-07 22:18:35 +0100504 """Generates the command to scale a Helm Chart release
aktas867418c2021-10-19 18:26:13 +0300505
Pedro Escaleira0a2060c2022-07-07 22:18:35 +0100506 Args:
507 kdu_model (str): Kdu model name, corresponding to the Helm local location or repository
508 kdu_instance (str): KDU instance, corresponding to the Helm Chart release in question
509 namespace (str): Namespace where this KDU instance is deployed
510 scale (int): Scale count
511 version (str): Constraint with specific version of the Chart to use
512 atomic (bool): If set, upgrade process rolls back changes made in case of failed upgrade.
513 The --wait flag will be set automatically if --atomic is used
514 replica_str (str): The key under resource_name key where the scale count is stored
515 timeout (float): The time, in seconds, to wait
516 resource_name (str): The KDU's resource to scale
517 kubeconfig (str): Kubeconfig file path
aktas867418c2021-10-19 18:26:13 +0300518
Pedro Escaleira0a2060c2022-07-07 22:18:35 +0100519 Returns:
520 str: command to scale a Helm Chart release
521 """
aktas867418c2021-10-19 18:26:13 +0300522
523 # scale
524 if resource_name:
525 scale_dict = {"{}.{}".format(resource_name, replica_str): scale}
526 else:
527 scale_dict = {replica_str: scale}
528
529 scale_str = self._params_to_set_option(scale_dict)
530
Pedro Escaleira0a2060c2022-07-07 22:18:35 +0100531 return self._get_upgrade_command(
532 kdu_model=kdu_model,
533 kdu_instance=kdu_instance,
534 namespace=namespace,
535 params_str=scale_str,
536 version=version,
537 atomic=atomic,
538 timeout=timeout,
aktas867418c2021-10-19 18:26:13 +0300539 kubeconfig=kubeconfig,
540 )
aktas867418c2021-10-19 18:26:13 +0300541
garciadeblas82b591c2021-03-24 09:22:13 +0100542 def _get_upgrade_command(
543 self,
544 kdu_model: str,
545 kdu_instance: str,
546 namespace: str,
547 params_str: str,
548 version: str,
549 atomic: bool,
550 timeout: float,
bravof7bd5c6a2021-11-17 11:14:57 -0300551 kubeconfig: str,
Gabriel Cuba085fa8d2022-10-10 12:13:55 -0500552 force: bool = False,
garciadeblas82b591c2021-03-24 09:22:13 +0100553 ) -> str:
Pedro Escaleira0a2060c2022-07-07 22:18:35 +0100554 """Generates the command to upgrade a Helm Chart release
555
556 Args:
557 kdu_model (str): Kdu model name, corresponding to the Helm local location or repository
558 kdu_instance (str): KDU instance, corresponding to the Helm Chart release in question
559 namespace (str): Namespace where this KDU instance is deployed
560 params_str (str): Params used to upgrade the Helm Chart release
561 version (str): Constraint with specific version of the Chart to use
562 atomic (bool): If set, upgrade process rolls back changes made in case of failed upgrade.
563 The --wait flag will be set automatically if --atomic is used
564 timeout (float): The time, in seconds, to wait
565 kubeconfig (str): Kubeconfig file path
Gabriel Cuba085fa8d2022-10-10 12:13:55 -0500566 force (bool): If set, helm forces resource updates through a replacement strategy. This may recreate pods.
Pedro Escaleira0a2060c2022-07-07 22:18:35 +0100567 Returns:
568 str: command to upgrade a Helm Chart release
569 """
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000570
571 timeout_str = ""
572 if timeout:
573 timeout_str = "--timeout {}s".format(timeout)
574
575 # atomic
576 atomic_str = ""
577 if atomic:
578 atomic_str = "--atomic"
579
Gabriel Cuba085fa8d2022-10-10 12:13:55 -0500580 # force
581 force_str = ""
582 if force:
583 force_str = "--force "
584
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000585 # version
586 version_str = ""
587 if version:
Daniel Arndtde6984b2023-06-27 16:42:41 -0300588 version_str = "--version {}".format(quote(version))
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000589
590 # namespace
591 namespace_str = ""
592 if namespace:
Daniel Arndtde6984b2023-06-27 16:42:41 -0300593 namespace_str = "--namespace {}".format(quote(namespace))
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000594
595 command = (
Gabriel Cuba085fa8d2022-10-10 12:13:55 -0500596 "env KUBECONFIG={kubeconfig} {helm} upgrade {name} {model} {namespace} {atomic} {force}"
Pedro Escaleira0a2060c2022-07-07 22:18:35 +0100597 "--output yaml {params} {timeout} --reuse-values {ver}"
bravof7bd5c6a2021-11-17 11:14:57 -0300598 ).format(
599 kubeconfig=kubeconfig,
600 helm=self._helm_command,
Daniel Arndtde6984b2023-06-27 16:42:41 -0300601 name=quote(kdu_instance),
bravof7bd5c6a2021-11-17 11:14:57 -0300602 namespace=namespace_str,
603 atomic=atomic_str,
Gabriel Cuba085fa8d2022-10-10 12:13:55 -0500604 force=force_str,
bravof7bd5c6a2021-11-17 11:14:57 -0300605 params=params_str,
606 timeout=timeout_str,
Daniel Arndtde6984b2023-06-27 16:42:41 -0300607 model=quote(kdu_model),
bravof7bd5c6a2021-11-17 11:14:57 -0300608 ver=version_str,
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000609 )
610 return command
611
garciadeblas82b591c2021-03-24 09:22:13 +0100612 def _get_rollback_command(
bravof7bd5c6a2021-11-17 11:14:57 -0300613 self, kdu_instance: str, namespace: str, revision: float, kubeconfig: str
garciadeblas82b591c2021-03-24 09:22:13 +0100614 ) -> str:
bravof7bd5c6a2021-11-17 11:14:57 -0300615 return "env KUBECONFIG={} {} rollback {} {} --namespace={} --wait".format(
Daniel Arndtde6984b2023-06-27 16:42:41 -0300616 kubeconfig,
617 self._helm_command,
618 quote(kdu_instance),
619 revision,
620 quote(namespace),
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000621 )
622
bravof7bd5c6a2021-11-17 11:14:57 -0300623 def _get_uninstall_command(
624 self, kdu_instance: str, namespace: str, kubeconfig: str
625 ) -> str:
bravof7bd5c6a2021-11-17 11:14:57 -0300626 return "env KUBECONFIG={} {} uninstall {} --namespace={}".format(
Daniel Arndtde6984b2023-06-27 16:42:41 -0300627 kubeconfig, self._helm_command, quote(kdu_instance), quote(namespace)
garciadeblas82b591c2021-03-24 09:22:13 +0100628 )
lloretgalleg095392b2020-11-20 11:28:08 +0000629
630 def _get_helm_chart_repos_ids(self, cluster_uuid) -> list:
631 repo_ids = []
632 cluster_filter = {"_admin.helm-chart-v3.id": cluster_uuid}
633 cluster = self.db.get_one("k8sclusters", cluster_filter)
634 if cluster:
635 repo_ids = cluster.get("_admin").get("helm_chart_repos") or []
636 return repo_ids
637 else:
638 raise K8sException(
639 "k8cluster with helm-id : {} not found".format(cluster_uuid)
640 )