blob: ad230b5f36a7ee859dd8d7e8c77c41664766fd2b [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,
lloretgalleg83e55892020-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,
lloretgalleg83e55892020-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...")
lloretgalleg83e55892020-12-17 12:42:11 +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,
David Garciac4da25c2021-02-23 11:47:29 +010095 kdu_instance: str,
lloretgalleg095392b2020-11-20 11:28:08 +000096 atomic: bool = True,
97 timeout: float = 300,
98 params: dict = None,
99 db_dict: dict = None,
100 kdu_name: str = None,
101 namespace: str = None,
102 ):
103 _, cluster_id = self._get_namespace_cluster_id(cluster_uuid)
104 self.log.debug("installing {} in cluster {}".format(kdu_model, cluster_id))
105
106 # sync local dir
107 self.fs.sync(from_path=cluster_id)
108
109 # init env, paths
110 paths, env = self._init_paths_env(
111 cluster_name=cluster_id, create_if_not_exist=True
112 )
113
David Garciac4da25c2021-02-23 11:47:29 +0100114 await self._install_impl(
115 cluster_id,
116 kdu_model,
117 paths,
118 env,
119 kdu_instance,
120 atomic=atomic,
121 timeout=timeout,
122 params=params,
123 db_dict=db_dict,
124 kdu_name=kdu_name,
125 namespace=namespace,
126 )
lloretgalleg095392b2020-11-20 11:28:08 +0000127
128 # sync fs
129 self.fs.reverse_sync(from_path=cluster_id)
130
131 self.log.debug("Returning kdu_instance {}".format(kdu_instance))
David Garciac4da25c2021-02-23 11:47:29 +0100132 return True
lloretgalleg095392b2020-11-20 11:28:08 +0000133
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000134 async def inspect_kdu(self, kdu_model: str, repo_url: str = None) -> str:
135
136 self.log.debug(
137 "inspect kdu_model {} from (optional) repo: {}".format(kdu_model, repo_url)
138 )
139
140 return await self._exec_inspect_comand(
141 inspect_command="", kdu_model=kdu_model, repo_url=repo_url
142 )
143
144 """
145 ####################################################################################
146 ################################### P R I V A T E ##################################
147 ####################################################################################
148 """
149
150 def _init_paths_env(self, cluster_name: str, create_if_not_exist: bool = True):
tiernoa5728bf2020-06-25 15:48:52 +0000151 """
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000152 Creates and returns base cluster and kube dirs and returns them.
153 Also created helm3 dirs according to new directory specification, paths are
154 returned and also environment variables that must be provided to execute commands
155
156 Helm 2 directory specification uses helm_home dir:
157
158 The variables assigned for this paths are:
159 - Helm hone: $HELM_HOME
160 - helm kubeconfig: $KUBECONFIG
161
162 :param cluster_name: cluster_name
163 :return: Dictionary with config_paths and dictionary with helm environment variables
tiernoa5728bf2020-06-25 15:48:52 +0000164 """
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000165 base = self.fs.path
166 if base.endswith("/") or base.endswith("\\"):
167 base = base[:-1]
tiernoa5728bf2020-06-25 15:48:52 +0000168
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000169 # base dir for cluster
170 cluster_dir = base + "/" + cluster_name
garciadeblas54771fa2019-12-13 13:39:03 +0100171
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000172 # kube dir
173 kube_dir = cluster_dir + "/" + ".kube"
174 if create_if_not_exist and not os.path.exists(kube_dir):
175 self.log.debug("Creating dir {}".format(kube_dir))
176 os.makedirs(kube_dir)
quilesj26c78a42019-10-28 18:10:42 +0100177
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000178 # helm home dir
179 helm_dir = cluster_dir + "/" + ".helm"
180 if create_if_not_exist and not os.path.exists(helm_dir):
181 self.log.debug("Creating dir {}".format(helm_dir))
182 os.makedirs(helm_dir)
quilesj26c78a42019-10-28 18:10:42 +0100183
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000184 config_filename = kube_dir + "/config"
quilesj26c78a42019-10-28 18:10:42 +0100185
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000186 # 2 - Prepare dictionary with paths
187 paths = {
188 "kube_dir": kube_dir,
189 "kube_config": config_filename,
190 "cluster_dir": cluster_dir,
191 "helm_dir": helm_dir,
192 }
193
194 for file_name, file in paths.items():
195 if "dir" in file_name and not os.path.exists(file):
196 err_msg = "{} dir does not exist".format(file)
197 self.log.error(err_msg)
198 raise K8sException(err_msg)
199
200 # 3 - Prepare environment variables
201 env = {"HELM_HOME": helm_dir, "KUBECONFIG": config_filename}
202
203 return paths, env
204
205 async def _get_services(self, cluster_id, kdu_instance, namespace):
206
207 # init config, env
208 paths, env = self._init_paths_env(
tiernoa5728bf2020-06-25 15:48:52 +0000209 cluster_name=cluster_id, create_if_not_exist=True
beierlmf52cb7c2020-04-21 16:36:35 -0400210 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000211
212 command1 = "{} get manifest {} ".format(self._helm_command, kdu_instance)
213 command2 = "{} get --namespace={} -f -".format(self.kubectl_command, namespace)
214 output, _rc = await self._local_async_exec_pipe(
215 command1, command2, env=env, raise_exception_on_error=True
216 )
217 services = self._parse_services(output)
218
219 return services
220
221 async def _cluster_init(self, cluster_id: str, namespace: str,
222 paths: dict, env: dict):
223 """
224 Implements the helm version dependent cluster initialization:
225 For helm2 it initialized tiller environment if needed
226 """
quilesj26c78a42019-10-28 18:10:42 +0100227
228 # check if tiller pod is up in cluster
beierlmf52cb7c2020-04-21 16:36:35 -0400229 command = "{} --kubeconfig={} --namespace={} get deployments".format(
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000230 self.kubectl_command, paths["kube_config"], namespace
beierlmf52cb7c2020-04-21 16:36:35 -0400231 )
232 output, _rc = await self._local_async_exec(
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000233 command=command, raise_exception_on_error=True, env=env
beierlmf52cb7c2020-04-21 16:36:35 -0400234 )
quilesj26c78a42019-10-28 18:10:42 +0100235
tierno119f7232020-04-21 13:22:26 +0000236 output_table = self._output_to_table(output=output)
quilesj26c78a42019-10-28 18:10:42 +0100237
238 # find 'tiller' pod in all pods
239 already_initialized = False
240 try:
241 for row in output_table:
beierlmf52cb7c2020-04-21 16:36:35 -0400242 if row[0].startswith("tiller-deploy"):
quilesj26c78a42019-10-28 18:10:42 +0100243 already_initialized = True
244 break
beierlmf52cb7c2020-04-21 16:36:35 -0400245 except Exception:
quilesj26c78a42019-10-28 18:10:42 +0100246 pass
247
248 # helm init
249 n2vc_installed_sw = False
250 if not already_initialized:
beierlmf52cb7c2020-04-21 16:36:35 -0400251 self.log.info(
tiernoa5728bf2020-06-25 15:48:52 +0000252 "Initializing helm in client and server: {}".format(cluster_id)
beierlmf52cb7c2020-04-21 16:36:35 -0400253 )
tiernoa5728bf2020-06-25 15:48:52 +0000254 command = "{} --kubeconfig={} --namespace kube-system create serviceaccount {}".format(
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000255 self.kubectl_command, paths["kube_config"], self.service_account
256 )
257 _, _rc = await self._local_async_exec(
258 command=command, raise_exception_on_error=False, env=env
259 )
tiernoa5728bf2020-06-25 15:48:52 +0000260
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000261 command = (
262 "{} --kubeconfig={} create clusterrolebinding osm-tiller-cluster-rule "
263 "--clusterrole=cluster-admin --serviceaccount=kube-system:{}"
264 ).format(self.kubectl_command, paths["kube_config"], self.service_account)
265 _, _rc = await self._local_async_exec(
266 command=command, raise_exception_on_error=False, env=env
267 )
tiernoa5728bf2020-06-25 15:48:52 +0000268
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000269 command = (
270 "{} --kubeconfig={} --tiller-namespace={} --home={} --service-account {} "
lloretgalleg83e55892020-12-17 12:42:11 +0000271 " --stable-repo-url {} init"
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000272 ).format(
273 self._helm_command,
274 paths["kube_config"],
275 namespace,
276 paths["helm_dir"],
277 self.service_account,
lloretgalleg83e55892020-12-17 12:42:11 +0000278 self._stable_repo_url
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000279 )
280 _, _rc = await self._local_async_exec(
281 command=command, raise_exception_on_error=True, env=env
282 )
quilesj26c78a42019-10-28 18:10:42 +0100283 n2vc_installed_sw = True
284 else:
285 # check client helm installation
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000286 check_file = paths["helm_dir"] + "/repository/repositories.yaml"
287 if not self._check_file_exists(
288 filename=check_file, exception_if_not_exists=False
289 ):
tiernoa5728bf2020-06-25 15:48:52 +0000290 self.log.info("Initializing helm in client: {}".format(cluster_id))
beierlmf52cb7c2020-04-21 16:36:35 -0400291 command = (
292 "{} --kubeconfig={} --tiller-namespace={} "
lloretgalleg83e55892020-12-17 12:42:11 +0000293 "--home={} init --client-only --stable-repo-url {} "
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000294 ).format(
295 self._helm_command,
296 paths["kube_config"],
297 namespace,
298 paths["helm_dir"],
lloretgalleg83e55892020-12-17 12:42:11 +0000299 self._stable_repo_url,
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000300 )
beierlmf52cb7c2020-04-21 16:36:35 -0400301 output, _rc = await self._local_async_exec(
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000302 command=command, raise_exception_on_error=True, env=env
beierlmf52cb7c2020-04-21 16:36:35 -0400303 )
quilesj26c78a42019-10-28 18:10:42 +0100304 else:
beierlmf52cb7c2020-04-21 16:36:35 -0400305 self.log.info("Helm client already initialized")
quilesj26c78a42019-10-28 18:10:42 +0100306
lloretgalleg83e55892020-12-17 12:42:11 +0000307 # remove old stable repo and add new one
308 cluster_uuid = "{}:{}".format(namespace, cluster_id)
309 repo_list = await self.repo_list(cluster_uuid)
310 for repo in repo_list:
311 if repo["name"] == "stable" and repo["url"] != self._stable_repo_url:
312 self.log.debug("Add new stable repo url: {}")
313 await self.repo_remove(cluster_uuid,
314 "stable")
315 await self.repo_add(cluster_uuid,
316 "stable",
317 self._stable_repo_url)
318 break
319
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000320 return n2vc_installed_sw
lloretgallege308c712020-09-02 09:40:38 +0000321
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000322 async def _uninstall_sw(self, cluster_id: str, namespace: str):
323 # uninstall Tiller if necessary
quilesj26c78a42019-10-28 18:10:42 +0100324
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000325 self.log.debug("Uninstalling tiller from cluster {}".format(cluster_id))
quilesj26c78a42019-10-28 18:10:42 +0100326
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000327 # init paths, env
328 paths, env = self._init_paths_env(
tiernoa5728bf2020-06-25 15:48:52 +0000329 cluster_name=cluster_id, create_if_not_exist=True
beierlmf52cb7c2020-04-21 16:36:35 -0400330 )
quilesj26c78a42019-10-28 18:10:42 +0100331
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000332 if not namespace:
333 # find namespace for tiller pod
334 command = "{} --kubeconfig={} get deployments --all-namespaces".format(
335 self.kubectl_command, paths["kube_config"]
336 )
337 output, _rc = await self._local_async_exec(
338 command=command, raise_exception_on_error=False, env=env
339 )
340 output_table = self._output_to_table(output=output)
341 namespace = None
342 for r in output_table:
343 try:
344 if "tiller-deploy" in r[1]:
345 namespace = r[0]
346 break
347 except Exception:
348 pass
quilesj26c78a42019-10-28 18:10:42 +0100349 else:
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000350 msg = "Tiller deployment not found in cluster {}".format(cluster_id)
351 self.log.error(msg)
quilesj26c78a42019-10-28 18:10:42 +0100352
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000353 self.log.debug("namespace for tiller: {}".format(namespace))
quilesj26c78a42019-10-28 18:10:42 +0100354
tierno53555f62020-04-07 11:08:16 +0000355 if namespace:
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000356 # uninstall tiller from cluster
357 self.log.debug("Uninstalling tiller from cluster {}".format(cluster_id))
358 command = "{} --kubeconfig={} --home={} reset".format(
359 self._helm_command, paths["kube_config"], paths["helm_dir"]
beierlmf52cb7c2020-04-21 16:36:35 -0400360 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000361 self.log.debug("resetting: {}".format(command))
362 output, _rc = await self._local_async_exec(
363 command=command, raise_exception_on_error=True, env=env
quilesj26c78a42019-10-28 18:10:42 +0100364 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000365 # Delete clusterrolebinding and serviceaccount.
366 # Ignore if errors for backward compatibility
367 command = (
368 "{} --kubeconfig={} delete clusterrolebinding.rbac.authorization.k8s."
369 "io/osm-tiller-cluster-rule"
370 ).format(self.kubectl_command, paths["kube_config"])
371 output, _rc = await self._local_async_exec(
372 command=command, raise_exception_on_error=False, env=env
quilesj26c78a42019-10-28 18:10:42 +0100373 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000374 command = "{} --kubeconfig={} --namespace kube-system delete serviceaccount/{}".format(
375 self.kubectl_command, paths["kube_config"], self.service_account
376 )
377 output, _rc = await self._local_async_exec(
378 command=command, raise_exception_on_error=False, env=env
379 )
quilesj26c78a42019-10-28 18:10:42 +0100380
381 else:
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000382 self.log.debug("namespace not found")
quilesj26c78a42019-10-28 18:10:42 +0100383
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000384 async def _instances_list(self, cluster_id):
quilesj26c78a42019-10-28 18:10:42 +0100385
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000386 # init paths, env
387 paths, env = self._init_paths_env(
tiernoa5728bf2020-06-25 15:48:52 +0000388 cluster_name=cluster_id, create_if_not_exist=True
beierlmf52cb7c2020-04-21 16:36:35 -0400389 )
quilesj26c78a42019-10-28 18:10:42 +0100390
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000391 command = "{} list --output yaml".format(self._helm_command)
quilesj26c78a42019-10-28 18:10:42 +0100392
beierlmf52cb7c2020-04-21 16:36:35 -0400393 output, _rc = await self._local_async_exec(
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000394 command=command, raise_exception_on_error=True, env=env
beierlmf52cb7c2020-04-21 16:36:35 -0400395 )
quilesj26c78a42019-10-28 18:10:42 +0100396
397 if output and len(output) > 0:
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000398 # parse yaml and update keys to lower case to unify with helm3
399 instances = yaml.load(output, Loader=yaml.SafeLoader).get("Releases")
400 new_instances = []
401 for instance in instances:
402 new_instance = dict((k.lower(), v) for k, v in instance.items())
403 new_instances.append(new_instance)
404 return new_instances
quilesj26c78a42019-10-28 18:10:42 +0100405 else:
406 return []
407
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000408 def _get_inspect_command(self, show_command: str, kdu_model: str, repo_str: str,
409 version: str):
410 inspect_command = "{} inspect {} {}{} {}".format(
411 self._helm_command, show_command, kdu_model, repo_str, version
beierlmf52cb7c2020-04-21 16:36:35 -0400412 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000413 return inspect_command
quilesj1be06302019-11-29 11:17:11 +0000414
quilesj26c78a42019-10-28 18:10:42 +0100415 async def _status_kdu(
beierlmf52cb7c2020-04-21 16:36:35 -0400416 self,
tiernoa5728bf2020-06-25 15:48:52 +0000417 cluster_id: str,
beierlmf52cb7c2020-04-21 16:36:35 -0400418 kdu_instance: str,
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000419 namespace: str = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400420 show_error_log: bool = False,
421 return_text: bool = False,
quilesj26c78a42019-10-28 18:10:42 +0100422 ):
423
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000424 self.log.debug(
425 "status of kdu_instance: {}, namespace: {} ".format(kdu_instance, namespace)
426 )
quilesj26c78a42019-10-28 18:10:42 +0100427
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000428 # init config, env
429 paths, env = self._init_paths_env(
tiernoa5728bf2020-06-25 15:48:52 +0000430 cluster_name=cluster_id, create_if_not_exist=True
beierlmf52cb7c2020-04-21 16:36:35 -0400431 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000432 command = "{} status {} --output yaml".format(self._helm_command, kdu_instance)
quilesj26c78a42019-10-28 18:10:42 +0100433 output, rc = await self._local_async_exec(
434 command=command,
435 raise_exception_on_error=True,
beierlmf52cb7c2020-04-21 16:36:35 -0400436 show_error_log=show_error_log,
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000437 env=env,
quilesj26c78a42019-10-28 18:10:42 +0100438 )
439
quilesj1be06302019-11-29 11:17:11 +0000440 if return_text:
441 return str(output)
442
quilesj26c78a42019-10-28 18:10:42 +0100443 if rc != 0:
444 return None
445
446 data = yaml.load(output, Loader=yaml.SafeLoader)
447
448 # remove field 'notes'
449 try:
beierlmf52cb7c2020-04-21 16:36:35 -0400450 del data.get("info").get("status")["notes"]
quilesj26c78a42019-10-28 18:10:42 +0100451 except KeyError:
452 pass
453
454 # parse field 'resources'
455 try:
beierlmf52cb7c2020-04-21 16:36:35 -0400456 resources = str(data.get("info").get("status").get("resources"))
quilesj26c78a42019-10-28 18:10:42 +0100457 resource_table = self._output_to_table(resources)
beierlmf52cb7c2020-04-21 16:36:35 -0400458 data.get("info").get("status")["resources"] = resource_table
459 except Exception:
quilesj26c78a42019-10-28 18:10:42 +0100460 pass
461
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000462 # set description to lowercase (unify with helm3)
463 try:
464 data.get("info")["description"] = data.get("info").pop("Description")
465 except KeyError:
466 pass
467
quilesj26c78a42019-10-28 18:10:42 +0100468 return data
469
lloretgalleg095392b2020-11-20 11:28:08 +0000470 def _get_helm_chart_repos_ids(self, cluster_uuid) -> list:
471 repo_ids = []
472 cluster_filter = {"_admin.helm-chart.id": cluster_uuid}
473 cluster = self.db.get_one("k8sclusters", cluster_filter)
474 if cluster:
475 repo_ids = cluster.get("_admin").get("helm_chart_repos") or []
476 return repo_ids
477 else:
478 raise K8sException(
479 "k8cluster with helm-id : {} not found".format(cluster_uuid)
480 )
481
tiernoa5728bf2020-06-25 15:48:52 +0000482 async def _is_install_completed(self, cluster_id: str, kdu_instance: str) -> bool:
quilesj26c78a42019-10-28 18:10:42 +0100483
beierlmf52cb7c2020-04-21 16:36:35 -0400484 status = await self._status_kdu(
tiernoa5728bf2020-06-25 15:48:52 +0000485 cluster_id=cluster_id, kdu_instance=kdu_instance, return_text=False
beierlmf52cb7c2020-04-21 16:36:35 -0400486 )
quilesj26c78a42019-10-28 18:10:42 +0100487
488 # extract info.status.resources-> str
489 # format:
490 # ==> v1/Deployment
491 # NAME READY UP-TO-DATE AVAILABLE AGE
492 # halting-horse-mongodb 0/1 1 0 0s
493 # halting-petit-mongodb 1/1 1 0 0s
494 # blank line
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000495 resources = K8sHelmBaseConnector._get_deep(
496 status, ("info", "status", "resources")
497 )
quilesj26c78a42019-10-28 18:10:42 +0100498
499 # convert to table
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000500 resources = K8sHelmBaseConnector._output_to_table(resources)
quilesj26c78a42019-10-28 18:10:42 +0100501
502 num_lines = len(resources)
503 index = 0
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000504 ready = True
quilesj26c78a42019-10-28 18:10:42 +0100505 while index < num_lines:
506 try:
507 line1 = resources[index]
508 index += 1
509 # find '==>' in column 0
beierlmf52cb7c2020-04-21 16:36:35 -0400510 if line1[0] == "==>":
quilesj26c78a42019-10-28 18:10:42 +0100511 line2 = resources[index]
512 index += 1
513 # find READY in column 1
beierlmf52cb7c2020-04-21 16:36:35 -0400514 if line2[1] == "READY":
quilesj26c78a42019-10-28 18:10:42 +0100515 # read next lines
516 line3 = resources[index]
517 index += 1
518 while len(line3) > 1 and index < num_lines:
519 ready_value = line3[1]
beierlmf52cb7c2020-04-21 16:36:35 -0400520 parts = ready_value.split(sep="/")
quilesj26c78a42019-10-28 18:10:42 +0100521 current = int(parts[0])
522 total = int(parts[1])
523 if current < total:
beierlmf52cb7c2020-04-21 16:36:35 -0400524 self.log.debug("NOT READY:\n {}".format(line3))
quilesj26c78a42019-10-28 18:10:42 +0100525 ready = False
526 line3 = resources[index]
527 index += 1
528
beierlmf52cb7c2020-04-21 16:36:35 -0400529 except Exception:
quilesj26c78a42019-10-28 18:10:42 +0100530 pass
531
532 return ready
533
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000534 def _get_install_command(
535 self, kdu_model, kdu_instance, namespace, params_str, version, atomic, timeout
536 ) -> str:
lloretgallegd99f3f22020-06-29 14:18:30 +0000537
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000538 timeout_str = ""
539 if timeout:
540 timeout_str = "--timeout {}".format(timeout)
lloretgallegd99f3f22020-06-29 14:18:30 +0000541
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000542 # atomic
543 atomic_str = ""
544 if atomic:
545 atomic_str = "--atomic"
546 # namespace
547 namespace_str = ""
548 if namespace:
549 namespace_str = "--namespace {}".format(namespace)
lloretgallegd99f3f22020-06-29 14:18:30 +0000550
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000551 # version
552 version_str = ""
553 if version:
554 version_str = version_str = "--version {}".format(version)
lloretgallegd99f3f22020-06-29 14:18:30 +0000555
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000556 command = (
557 "{helm} install {atomic} --output yaml "
558 "{params} {timeout} --name={name} {ns} {model} {ver}".format(
559 helm=self._helm_command,
560 atomic=atomic_str,
561 params=params_str,
562 timeout=timeout_str,
563 name=kdu_instance,
564 ns=namespace_str,
565 model=kdu_model,
566 ver=version_str,
beierlmf52cb7c2020-04-21 16:36:35 -0400567 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000568 )
569 return command
quilesj26c78a42019-10-28 18:10:42 +0100570
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000571 def _get_upgrade_command(
572 self, kdu_model, kdu_instance, namespace, params_str, version, atomic, timeout
573 ) -> str:
quilesj26c78a42019-10-28 18:10:42 +0100574
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000575 timeout_str = ""
576 if timeout:
577 timeout_str = "--timeout {}".format(timeout)
quilesj26c78a42019-10-28 18:10:42 +0100578
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000579 # atomic
580 atomic_str = ""
581 if atomic:
582 atomic_str = "--atomic"
quilesj26c78a42019-10-28 18:10:42 +0100583
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000584 # version
585 version_str = ""
586 if version:
587 version_str = "--version {}".format(version)
quilesj26c78a42019-10-28 18:10:42 +0100588
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000589 command = "{helm} upgrade {atomic} --output yaml {params} {timeout} {name} {model} {ver}"\
590 .format(helm=self._helm_command,
591 atomic=atomic_str,
592 params=params_str,
593 timeout=timeout_str,
594 name=kdu_instance,
595 model=kdu_model,
596 ver=version_str
597 )
598 return command
quilesj26c78a42019-10-28 18:10:42 +0100599
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000600 def _get_rollback_command(self, kdu_instance, namespace, revision) -> str:
601 return "{} rollback {} {} --wait".format(
602 self._helm_command, kdu_instance, revision
603 )
quilesj26c78a42019-10-28 18:10:42 +0100604
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000605 def _get_uninstall_command(self, kdu_instance: str, namespace: str) -> str:
606 return "{} delete --purge {}".format(self._helm_command, kdu_instance)