blob: fdae32f39859784c41097f006bdba19b7140c78c [file] [log] [blame]
quilesj26c78a42019-10-28 18:10:42 +01001##
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##
quilesj26c78a42019-10-28 18:10:42 +010022import asyncio
beierlmf52cb7c2020-04-21 16:36:35 -040023import os
beierlmf52cb7c2020-04-21 16:36:35 -040024import yaml
quilesj26c78a42019-10-28 18:10:42 +010025
lloretgalleg1c83f2e2020-10-22 09:12:35 +000026from n2vc.k8s_helm_base_conn import K8sHelmBaseConnector
27from n2vc.exceptions import K8sException
quilesj26c78a42019-10-28 18:10:42 +010028
lloretgalleg1c83f2e2020-10-22 09:12:35 +000029
30class K8sHelmConnector(K8sHelmBaseConnector):
quilesj26c78a42019-10-28 18:10:42 +010031
32 """
beierlmf52cb7c2020-04-21 16:36:35 -040033 ####################################################################################
34 ################################### P U B L I C ####################################
35 ####################################################################################
quilesj26c78a42019-10-28 18:10:42 +010036 """
37
38 def __init__(
beierlmf52cb7c2020-04-21 16:36:35 -040039 self,
40 fs: object,
41 db: object,
42 kubectl_command: str = "/usr/bin/kubectl",
43 helm_command: str = "/usr/bin/helm",
44 log: object = None,
45 on_update_db=None,
lloretgalleg137dc9e2020-12-17 12:42:11 +000046 vca_config: dict = None,
quilesj26c78a42019-10-28 18:10:42 +010047 ):
48 """
lloretgalleg1c83f2e2020-10-22 09:12:35 +000049 Initializes helm connector for helm v2
quilesj26c78a42019-10-28 18:10:42 +010050
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
lloretgalleg1c83f2e2020-10-22 09:12:35 +000060 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,
lloretgalleg137dc9e2020-12-17 12:42:11 +000068 vca_config=vca_config,
lloretgalleg1c83f2e2020-10-22 09:12:35 +000069 )
quilesj26c78a42019-10-28 18:10:42 +010070
lloretgalleg1c83f2e2020-10-22 09:12:35 +000071 self.log.info("Initializing K8S Helm2 connector")
quilesj26c78a42019-10-28 18:10:42 +010072
quilesj1be06302019-11-29 11:17:11 +000073 # initialize helm client-only
beierlmf52cb7c2020-04-21 16:36:35 -040074 self.log.debug("Initializing helm client-only...")
lloretgallegee5c9a12021-01-15 11:33:00 +000075 command = "{} init --client-only --stable-repo-url {} ".format(
76 self._helm_command, self._stable_repo_url)
quilesj1be06302019-11-29 11:17:11 +000077 try:
beierlmf52cb7c2020-04-21 16:36:35 -040078 asyncio.ensure_future(
79 self._local_async_exec(command=command, raise_exception_on_error=False)
80 )
quilesj1be06302019-11-29 11:17:11 +000081 # loop = asyncio.get_event_loop()
beierlmf52cb7c2020-04-21 16:36:35 -040082 # loop.run_until_complete(self._local_async_exec(command=command,
83 # raise_exception_on_error=False))
quilesj1be06302019-11-29 11:17:11 +000084 except Exception as e:
beierlmf52cb7c2020-04-21 16:36:35 -040085 self.warning(
86 msg="helm init failed (it was already initialized): {}".format(e)
87 )
quilesj1be06302019-11-29 11:17:11 +000088
lloretgalleg1c83f2e2020-10-22 09:12:35 +000089 self.log.info("K8S Helm2 connector initialized")
quilesj26c78a42019-10-28 18:10:42 +010090
lloretgalleg095392b2020-11-20 11:28:08 +000091 async def install(
92 self,
93 cluster_uuid: str,
94 kdu_model: str,
95 atomic: bool = True,
96 timeout: float = 300,
97 params: dict = None,
98 db_dict: dict = None,
99 kdu_name: str = None,
100 namespace: str = None,
101 ):
102 _, cluster_id = self._get_namespace_cluster_id(cluster_uuid)
103 self.log.debug("installing {} in cluster {}".format(kdu_model, cluster_id))
104
105 # sync local dir
106 self.fs.sync(from_path=cluster_id)
107
108 # init env, paths
109 paths, env = self._init_paths_env(
110 cluster_name=cluster_id, create_if_not_exist=True
111 )
112
113 kdu_instance = await self._install_impl(cluster_id,
114 kdu_model,
115 paths,
116 env,
117 atomic=atomic,
118 timeout=timeout,
119 params=params,
120 db_dict=db_dict,
121 kdu_name=kdu_name,
122 namespace=namespace)
123
124 # sync fs
125 self.fs.reverse_sync(from_path=cluster_id)
126
127 self.log.debug("Returning kdu_instance {}".format(kdu_instance))
128 return kdu_instance
129
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000130 async def inspect_kdu(self, kdu_model: str, repo_url: str = None) -> str:
131
132 self.log.debug(
133 "inspect kdu_model {} from (optional) repo: {}".format(kdu_model, repo_url)
134 )
135
136 return await self._exec_inspect_comand(
137 inspect_command="", kdu_model=kdu_model, repo_url=repo_url
138 )
139
140 """
141 ####################################################################################
142 ################################### P R I V A T E ##################################
143 ####################################################################################
144 """
145
146 def _init_paths_env(self, cluster_name: str, create_if_not_exist: bool = True):
tiernoa5728bf2020-06-25 15:48:52 +0000147 """
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000148 Creates and returns base cluster and kube dirs and returns them.
149 Also created helm3 dirs according to new directory specification, paths are
150 returned and also environment variables that must be provided to execute commands
151
152 Helm 2 directory specification uses helm_home dir:
153
154 The variables assigned for this paths are:
155 - Helm hone: $HELM_HOME
156 - helm kubeconfig: $KUBECONFIG
157
158 :param cluster_name: cluster_name
159 :return: Dictionary with config_paths and dictionary with helm environment variables
tiernoa5728bf2020-06-25 15:48:52 +0000160 """
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000161 base = self.fs.path
162 if base.endswith("/") or base.endswith("\\"):
163 base = base[:-1]
tiernoa5728bf2020-06-25 15:48:52 +0000164
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000165 # base dir for cluster
166 cluster_dir = base + "/" + cluster_name
garciadeblas54771fa2019-12-13 13:39:03 +0100167
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000168 # kube dir
169 kube_dir = cluster_dir + "/" + ".kube"
170 if create_if_not_exist and not os.path.exists(kube_dir):
171 self.log.debug("Creating dir {}".format(kube_dir))
172 os.makedirs(kube_dir)
quilesj26c78a42019-10-28 18:10:42 +0100173
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000174 # helm home dir
175 helm_dir = cluster_dir + "/" + ".helm"
176 if create_if_not_exist and not os.path.exists(helm_dir):
177 self.log.debug("Creating dir {}".format(helm_dir))
178 os.makedirs(helm_dir)
quilesj26c78a42019-10-28 18:10:42 +0100179
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000180 config_filename = kube_dir + "/config"
quilesj26c78a42019-10-28 18:10:42 +0100181
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000182 # 2 - Prepare dictionary with paths
183 paths = {
184 "kube_dir": kube_dir,
185 "kube_config": config_filename,
186 "cluster_dir": cluster_dir,
187 "helm_dir": helm_dir,
188 }
189
190 for file_name, file in paths.items():
191 if "dir" in file_name and not os.path.exists(file):
192 err_msg = "{} dir does not exist".format(file)
193 self.log.error(err_msg)
194 raise K8sException(err_msg)
195
196 # 3 - Prepare environment variables
197 env = {"HELM_HOME": helm_dir, "KUBECONFIG": config_filename}
198
199 return paths, env
200
201 async def _get_services(self, cluster_id, kdu_instance, namespace):
202
203 # init config, env
204 paths, env = self._init_paths_env(
tiernoa5728bf2020-06-25 15:48:52 +0000205 cluster_name=cluster_id, create_if_not_exist=True
beierlmf52cb7c2020-04-21 16:36:35 -0400206 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000207
208 command1 = "{} get manifest {} ".format(self._helm_command, kdu_instance)
209 command2 = "{} get --namespace={} -f -".format(self.kubectl_command, namespace)
210 output, _rc = await self._local_async_exec_pipe(
211 command1, command2, env=env, raise_exception_on_error=True
212 )
213 services = self._parse_services(output)
214
215 return services
216
217 async def _cluster_init(self, cluster_id: str, namespace: str,
218 paths: dict, env: dict):
219 """
220 Implements the helm version dependent cluster initialization:
221 For helm2 it initialized tiller environment if needed
222 """
quilesj26c78a42019-10-28 18:10:42 +0100223
224 # check if tiller pod is up in cluster
beierlmf52cb7c2020-04-21 16:36:35 -0400225 command = "{} --kubeconfig={} --namespace={} get deployments".format(
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000226 self.kubectl_command, paths["kube_config"], namespace
beierlmf52cb7c2020-04-21 16:36:35 -0400227 )
228 output, _rc = await self._local_async_exec(
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000229 command=command, raise_exception_on_error=True, env=env
beierlmf52cb7c2020-04-21 16:36:35 -0400230 )
quilesj26c78a42019-10-28 18:10:42 +0100231
tierno119f7232020-04-21 13:22:26 +0000232 output_table = self._output_to_table(output=output)
quilesj26c78a42019-10-28 18:10:42 +0100233
234 # find 'tiller' pod in all pods
235 already_initialized = False
236 try:
237 for row in output_table:
beierlmf52cb7c2020-04-21 16:36:35 -0400238 if row[0].startswith("tiller-deploy"):
quilesj26c78a42019-10-28 18:10:42 +0100239 already_initialized = True
240 break
beierlmf52cb7c2020-04-21 16:36:35 -0400241 except Exception:
quilesj26c78a42019-10-28 18:10:42 +0100242 pass
243
244 # helm init
245 n2vc_installed_sw = False
246 if not already_initialized:
beierlmf52cb7c2020-04-21 16:36:35 -0400247 self.log.info(
tiernoa5728bf2020-06-25 15:48:52 +0000248 "Initializing helm in client and server: {}".format(cluster_id)
beierlmf52cb7c2020-04-21 16:36:35 -0400249 )
tiernoa5728bf2020-06-25 15:48:52 +0000250 command = "{} --kubeconfig={} --namespace kube-system create serviceaccount {}".format(
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000251 self.kubectl_command, paths["kube_config"], self.service_account
252 )
253 _, _rc = await self._local_async_exec(
254 command=command, raise_exception_on_error=False, env=env
255 )
tiernoa5728bf2020-06-25 15:48:52 +0000256
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000257 command = (
258 "{} --kubeconfig={} create clusterrolebinding osm-tiller-cluster-rule "
259 "--clusterrole=cluster-admin --serviceaccount=kube-system:{}"
260 ).format(self.kubectl_command, paths["kube_config"], self.service_account)
261 _, _rc = await self._local_async_exec(
262 command=command, raise_exception_on_error=False, env=env
263 )
tiernoa5728bf2020-06-25 15:48:52 +0000264
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000265 command = (
266 "{} --kubeconfig={} --tiller-namespace={} --home={} --service-account {} "
lloretgalleg137dc9e2020-12-17 12:42:11 +0000267 " --stable-repo-url {} init"
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000268 ).format(
269 self._helm_command,
270 paths["kube_config"],
271 namespace,
272 paths["helm_dir"],
273 self.service_account,
lloretgalleg137dc9e2020-12-17 12:42:11 +0000274 self._stable_repo_url
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000275 )
276 _, _rc = await self._local_async_exec(
277 command=command, raise_exception_on_error=True, env=env
278 )
quilesj26c78a42019-10-28 18:10:42 +0100279 n2vc_installed_sw = True
280 else:
281 # check client helm installation
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000282 check_file = paths["helm_dir"] + "/repository/repositories.yaml"
283 if not self._check_file_exists(
284 filename=check_file, exception_if_not_exists=False
285 ):
tiernoa5728bf2020-06-25 15:48:52 +0000286 self.log.info("Initializing helm in client: {}".format(cluster_id))
beierlmf52cb7c2020-04-21 16:36:35 -0400287 command = (
288 "{} --kubeconfig={} --tiller-namespace={} "
lloretgallegee5c9a12021-01-15 11:33:00 +0000289 "--home={} init --client-only --stable-repo-url {} "
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000290 ).format(
291 self._helm_command,
292 paths["kube_config"],
293 namespace,
294 paths["helm_dir"],
lloretgallegee5c9a12021-01-15 11:33:00 +0000295 self._stable_repo_url,
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000296 )
beierlmf52cb7c2020-04-21 16:36:35 -0400297 output, _rc = await self._local_async_exec(
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000298 command=command, raise_exception_on_error=True, env=env
beierlmf52cb7c2020-04-21 16:36:35 -0400299 )
quilesj26c78a42019-10-28 18:10:42 +0100300 else:
beierlmf52cb7c2020-04-21 16:36:35 -0400301 self.log.info("Helm client already initialized")
quilesj26c78a42019-10-28 18:10:42 +0100302
lloretgalleg137dc9e2020-12-17 12:42:11 +0000303 # remove old stable repo and add new one
304 cluster_uuid = "{}:{}".format(namespace, cluster_id)
305 repo_list = await self.repo_list(cluster_uuid)
306 for repo in repo_list:
307 if repo["name"] == "stable" and repo["url"] != self._stable_repo_url:
308 self.log.debug("Add new stable repo url: {}")
309 await self.repo_remove(cluster_uuid,
310 "stable")
311 await self.repo_add(cluster_uuid,
312 "stable",
313 self._stable_repo_url)
314 break
315
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000316 return n2vc_installed_sw
lloretgallege308c712020-09-02 09:40:38 +0000317
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000318 async def _uninstall_sw(self, cluster_id: str, namespace: str):
319 # uninstall Tiller if necessary
quilesj26c78a42019-10-28 18:10:42 +0100320
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000321 self.log.debug("Uninstalling tiller from cluster {}".format(cluster_id))
quilesj26c78a42019-10-28 18:10:42 +0100322
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000323 # init paths, env
324 paths, env = self._init_paths_env(
tiernoa5728bf2020-06-25 15:48:52 +0000325 cluster_name=cluster_id, create_if_not_exist=True
beierlmf52cb7c2020-04-21 16:36:35 -0400326 )
quilesj26c78a42019-10-28 18:10:42 +0100327
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000328 if not namespace:
329 # find namespace for tiller pod
330 command = "{} --kubeconfig={} get deployments --all-namespaces".format(
331 self.kubectl_command, paths["kube_config"]
332 )
333 output, _rc = await self._local_async_exec(
334 command=command, raise_exception_on_error=False, env=env
335 )
336 output_table = self._output_to_table(output=output)
337 namespace = None
338 for r in output_table:
339 try:
340 if "tiller-deploy" in r[1]:
341 namespace = r[0]
342 break
343 except Exception:
344 pass
quilesj26c78a42019-10-28 18:10:42 +0100345 else:
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000346 msg = "Tiller deployment not found in cluster {}".format(cluster_id)
347 self.log.error(msg)
quilesj26c78a42019-10-28 18:10:42 +0100348
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000349 self.log.debug("namespace for tiller: {}".format(namespace))
quilesj26c78a42019-10-28 18:10:42 +0100350
tierno53555f62020-04-07 11:08:16 +0000351 if namespace:
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000352 # uninstall tiller from cluster
353 self.log.debug("Uninstalling tiller from cluster {}".format(cluster_id))
354 command = "{} --kubeconfig={} --home={} reset".format(
355 self._helm_command, paths["kube_config"], paths["helm_dir"]
beierlmf52cb7c2020-04-21 16:36:35 -0400356 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000357 self.log.debug("resetting: {}".format(command))
358 output, _rc = await self._local_async_exec(
359 command=command, raise_exception_on_error=True, env=env
quilesj26c78a42019-10-28 18:10:42 +0100360 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000361 # Delete clusterrolebinding and serviceaccount.
362 # Ignore if errors for backward compatibility
363 command = (
364 "{} --kubeconfig={} delete clusterrolebinding.rbac.authorization.k8s."
365 "io/osm-tiller-cluster-rule"
366 ).format(self.kubectl_command, paths["kube_config"])
367 output, _rc = await self._local_async_exec(
368 command=command, raise_exception_on_error=False, env=env
quilesj26c78a42019-10-28 18:10:42 +0100369 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000370 command = "{} --kubeconfig={} --namespace kube-system delete serviceaccount/{}".format(
371 self.kubectl_command, paths["kube_config"], self.service_account
372 )
373 output, _rc = await self._local_async_exec(
374 command=command, raise_exception_on_error=False, env=env
375 )
quilesj26c78a42019-10-28 18:10:42 +0100376
377 else:
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000378 self.log.debug("namespace not found")
quilesj26c78a42019-10-28 18:10:42 +0100379
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000380 async def _instances_list(self, cluster_id):
quilesj26c78a42019-10-28 18:10:42 +0100381
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000382 # init paths, env
383 paths, env = self._init_paths_env(
tiernoa5728bf2020-06-25 15:48:52 +0000384 cluster_name=cluster_id, create_if_not_exist=True
beierlmf52cb7c2020-04-21 16:36:35 -0400385 )
quilesj26c78a42019-10-28 18:10:42 +0100386
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000387 command = "{} list --output yaml".format(self._helm_command)
quilesj26c78a42019-10-28 18:10:42 +0100388
beierlmf52cb7c2020-04-21 16:36:35 -0400389 output, _rc = await self._local_async_exec(
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000390 command=command, raise_exception_on_error=True, env=env
beierlmf52cb7c2020-04-21 16:36:35 -0400391 )
quilesj26c78a42019-10-28 18:10:42 +0100392
393 if output and len(output) > 0:
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000394 # parse yaml and update keys to lower case to unify with helm3
395 instances = yaml.load(output, Loader=yaml.SafeLoader).get("Releases")
396 new_instances = []
397 for instance in instances:
398 new_instance = dict((k.lower(), v) for k, v in instance.items())
399 new_instances.append(new_instance)
400 return new_instances
quilesj26c78a42019-10-28 18:10:42 +0100401 else:
402 return []
403
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000404 def _get_inspect_command(self, show_command: str, kdu_model: str, repo_str: str,
405 version: str):
406 inspect_command = "{} inspect {} {}{} {}".format(
407 self._helm_command, show_command, kdu_model, repo_str, version
beierlmf52cb7c2020-04-21 16:36:35 -0400408 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000409 return inspect_command
quilesj1be06302019-11-29 11:17:11 +0000410
quilesj26c78a42019-10-28 18:10:42 +0100411 async def _status_kdu(
beierlmf52cb7c2020-04-21 16:36:35 -0400412 self,
tiernoa5728bf2020-06-25 15:48:52 +0000413 cluster_id: str,
beierlmf52cb7c2020-04-21 16:36:35 -0400414 kdu_instance: str,
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000415 namespace: str = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400416 show_error_log: bool = False,
417 return_text: bool = False,
quilesj26c78a42019-10-28 18:10:42 +0100418 ):
419
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000420 self.log.debug(
421 "status of kdu_instance: {}, namespace: {} ".format(kdu_instance, namespace)
422 )
quilesj26c78a42019-10-28 18:10:42 +0100423
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000424 # init config, env
425 paths, env = self._init_paths_env(
tiernoa5728bf2020-06-25 15:48:52 +0000426 cluster_name=cluster_id, create_if_not_exist=True
beierlmf52cb7c2020-04-21 16:36:35 -0400427 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000428 command = "{} status {} --output yaml".format(self._helm_command, kdu_instance)
quilesj26c78a42019-10-28 18:10:42 +0100429 output, rc = await self._local_async_exec(
430 command=command,
431 raise_exception_on_error=True,
beierlmf52cb7c2020-04-21 16:36:35 -0400432 show_error_log=show_error_log,
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000433 env=env,
quilesj26c78a42019-10-28 18:10:42 +0100434 )
435
quilesj1be06302019-11-29 11:17:11 +0000436 if return_text:
437 return str(output)
438
quilesj26c78a42019-10-28 18:10:42 +0100439 if rc != 0:
440 return None
441
442 data = yaml.load(output, Loader=yaml.SafeLoader)
443
444 # remove field 'notes'
445 try:
beierlmf52cb7c2020-04-21 16:36:35 -0400446 del data.get("info").get("status")["notes"]
quilesj26c78a42019-10-28 18:10:42 +0100447 except KeyError:
448 pass
449
450 # parse field 'resources'
451 try:
beierlmf52cb7c2020-04-21 16:36:35 -0400452 resources = str(data.get("info").get("status").get("resources"))
quilesj26c78a42019-10-28 18:10:42 +0100453 resource_table = self._output_to_table(resources)
beierlmf52cb7c2020-04-21 16:36:35 -0400454 data.get("info").get("status")["resources"] = resource_table
455 except Exception:
quilesj26c78a42019-10-28 18:10:42 +0100456 pass
457
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000458 # set description to lowercase (unify with helm3)
459 try:
460 data.get("info")["description"] = data.get("info").pop("Description")
461 except KeyError:
462 pass
463
quilesj26c78a42019-10-28 18:10:42 +0100464 return data
465
lloretgalleg095392b2020-11-20 11:28:08 +0000466 def _get_helm_chart_repos_ids(self, cluster_uuid) -> list:
467 repo_ids = []
468 cluster_filter = {"_admin.helm-chart.id": cluster_uuid}
469 cluster = self.db.get_one("k8sclusters", cluster_filter)
470 if cluster:
471 repo_ids = cluster.get("_admin").get("helm_chart_repos") or []
472 return repo_ids
473 else:
474 raise K8sException(
475 "k8cluster with helm-id : {} not found".format(cluster_uuid)
476 )
477
tiernoa5728bf2020-06-25 15:48:52 +0000478 async def _is_install_completed(self, cluster_id: str, kdu_instance: str) -> bool:
quilesj26c78a42019-10-28 18:10:42 +0100479
beierlmf52cb7c2020-04-21 16:36:35 -0400480 status = await self._status_kdu(
tiernoa5728bf2020-06-25 15:48:52 +0000481 cluster_id=cluster_id, kdu_instance=kdu_instance, return_text=False
beierlmf52cb7c2020-04-21 16:36:35 -0400482 )
quilesj26c78a42019-10-28 18:10:42 +0100483
484 # extract info.status.resources-> str
485 # format:
486 # ==> v1/Deployment
487 # NAME READY UP-TO-DATE AVAILABLE AGE
488 # halting-horse-mongodb 0/1 1 0 0s
489 # halting-petit-mongodb 1/1 1 0 0s
490 # blank line
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000491 resources = K8sHelmBaseConnector._get_deep(
492 status, ("info", "status", "resources")
493 )
quilesj26c78a42019-10-28 18:10:42 +0100494
495 # convert to table
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000496 resources = K8sHelmBaseConnector._output_to_table(resources)
quilesj26c78a42019-10-28 18:10:42 +0100497
498 num_lines = len(resources)
499 index = 0
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000500 ready = True
quilesj26c78a42019-10-28 18:10:42 +0100501 while index < num_lines:
502 try:
503 line1 = resources[index]
504 index += 1
505 # find '==>' in column 0
beierlmf52cb7c2020-04-21 16:36:35 -0400506 if line1[0] == "==>":
quilesj26c78a42019-10-28 18:10:42 +0100507 line2 = resources[index]
508 index += 1
509 # find READY in column 1
beierlmf52cb7c2020-04-21 16:36:35 -0400510 if line2[1] == "READY":
quilesj26c78a42019-10-28 18:10:42 +0100511 # read next lines
512 line3 = resources[index]
513 index += 1
514 while len(line3) > 1 and index < num_lines:
515 ready_value = line3[1]
beierlmf52cb7c2020-04-21 16:36:35 -0400516 parts = ready_value.split(sep="/")
quilesj26c78a42019-10-28 18:10:42 +0100517 current = int(parts[0])
518 total = int(parts[1])
519 if current < total:
beierlmf52cb7c2020-04-21 16:36:35 -0400520 self.log.debug("NOT READY:\n {}".format(line3))
quilesj26c78a42019-10-28 18:10:42 +0100521 ready = False
522 line3 = resources[index]
523 index += 1
524
beierlmf52cb7c2020-04-21 16:36:35 -0400525 except Exception:
quilesj26c78a42019-10-28 18:10:42 +0100526 pass
527
528 return ready
529
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000530 def _get_install_command(
531 self, kdu_model, kdu_instance, namespace, params_str, version, atomic, timeout
532 ) -> str:
lloretgallegd99f3f22020-06-29 14:18:30 +0000533
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000534 timeout_str = ""
535 if timeout:
536 timeout_str = "--timeout {}".format(timeout)
lloretgallegd99f3f22020-06-29 14:18:30 +0000537
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000538 # atomic
539 atomic_str = ""
540 if atomic:
541 atomic_str = "--atomic"
542 # namespace
543 namespace_str = ""
544 if namespace:
545 namespace_str = "--namespace {}".format(namespace)
lloretgallegd99f3f22020-06-29 14:18:30 +0000546
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000547 # version
548 version_str = ""
549 if version:
550 version_str = version_str = "--version {}".format(version)
lloretgallegd99f3f22020-06-29 14:18:30 +0000551
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000552 command = (
553 "{helm} install {atomic} --output yaml "
554 "{params} {timeout} --name={name} {ns} {model} {ver}".format(
555 helm=self._helm_command,
556 atomic=atomic_str,
557 params=params_str,
558 timeout=timeout_str,
559 name=kdu_instance,
560 ns=namespace_str,
561 model=kdu_model,
562 ver=version_str,
beierlmf52cb7c2020-04-21 16:36:35 -0400563 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000564 )
565 return command
quilesj26c78a42019-10-28 18:10:42 +0100566
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000567 def _get_upgrade_command(
568 self, kdu_model, kdu_instance, namespace, params_str, version, atomic, timeout
569 ) -> str:
quilesj26c78a42019-10-28 18:10:42 +0100570
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000571 timeout_str = ""
572 if timeout:
573 timeout_str = "--timeout {}".format(timeout)
quilesj26c78a42019-10-28 18:10:42 +0100574
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000575 # atomic
576 atomic_str = ""
577 if atomic:
578 atomic_str = "--atomic"
quilesj26c78a42019-10-28 18:10:42 +0100579
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000580 # version
581 version_str = ""
582 if version:
583 version_str = "--version {}".format(version)
quilesj26c78a42019-10-28 18:10:42 +0100584
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000585 command = "{helm} upgrade {atomic} --output yaml {params} {timeout} {name} {model} {ver}"\
586 .format(helm=self._helm_command,
587 atomic=atomic_str,
588 params=params_str,
589 timeout=timeout_str,
590 name=kdu_instance,
591 model=kdu_model,
592 ver=version_str
593 )
594 return command
quilesj26c78a42019-10-28 18:10:42 +0100595
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000596 def _get_rollback_command(self, kdu_instance, namespace, revision) -> str:
597 return "{} rollback {} {} --wait".format(
598 self._helm_command, kdu_instance, revision
599 )
quilesj26c78a42019-10-28 18:10:42 +0100600
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000601 def _get_uninstall_command(self, kdu_instance: str, namespace: str) -> str:
602 return "{} delete --purge {}".format(self._helm_command, kdu_instance)