X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=n2vc%2Fk8s_helm_base_conn.py;h=2a588b4f49ddc724ec3948eacc74429681b56c17;hb=d21509c14b0bef94d5ace71a4b5c7592851b453d;hp=e89d6fa2d68a1aac9e2d85877275372ad79684c3;hpb=0a2060ce3db8e457c732c83b87030923cbac6e11;p=osm%2FN2VC.git diff --git a/n2vc/k8s_helm_base_conn.py b/n2vc/k8s_helm_base_conn.py index e89d6fa..2a588b4 100644 --- a/n2vc/k8s_helm_base_conn.py +++ b/n2vc/k8s_helm_base_conn.py @@ -34,6 +34,7 @@ from uuid import uuid4 from n2vc.config import EnvironConfig from n2vc.exceptions import K8sException from n2vc.k8s_conn import K8sConnector +from n2vc.kubectl import Kubectl class K8sHelmBaseConnector(K8sConnector): @@ -449,7 +450,6 @@ class K8sHelmBaseConnector(K8sConnector): output, rc = exec_task.result() else: - output, rc = await self._local_async_exec( command=command, raise_exception_on_error=False, env=env ) @@ -481,6 +481,8 @@ class K8sHelmBaseConnector(K8sConnector): timeout: float = 300, params: dict = None, db_dict: dict = None, + namespace: str = None, + force: bool = False, ): self.log.debug("upgrading {} in cluster {}".format(kdu_model, cluster_uuid)) @@ -488,9 +490,13 @@ class K8sHelmBaseConnector(K8sConnector): self.fs.sync(from_path=cluster_uuid) # look for instance to obtain namespace - instance_info = await self.get_instance_info(cluster_uuid, kdu_instance) - if not instance_info: - raise K8sException("kdu_instance {} not found".format(kdu_instance)) + + # set namespace + if not namespace: + instance_info = await self.get_instance_info(cluster_uuid, kdu_instance) + if not instance_info: + raise K8sException("kdu_instance {} not found".format(kdu_instance)) + namespace = instance_info["namespace"] # init env, paths paths, env = self._init_paths_env( @@ -515,18 +521,18 @@ class K8sHelmBaseConnector(K8sConnector): command = self._get_upgrade_command( kdu_model, kdu_instance, - instance_info["namespace"], + namespace, params_str, version, atomic, timeout, paths["kube_config"], + force, ) self.log.debug("upgrading: {}".format(command)) if atomic: - # exec helm in a task exec_task = asyncio.ensure_future( coro_or_future=self._local_async_exec( @@ -538,7 +544,7 @@ class K8sHelmBaseConnector(K8sConnector): coro_or_future=self._store_status( cluster_id=cluster_uuid, kdu_instance=kdu_instance, - namespace=instance_info["namespace"], + namespace=namespace, db_dict=db_dict, operation="upgrade", ) @@ -552,7 +558,6 @@ class K8sHelmBaseConnector(K8sConnector): output, rc = exec_task.result() else: - output, rc = await self._local_async_exec( command=command, raise_exception_on_error=False, env=env ) @@ -565,7 +570,7 @@ class K8sHelmBaseConnector(K8sConnector): await self._store_status( cluster_id=cluster_uuid, kdu_instance=kdu_instance, - namespace=instance_info["namespace"], + namespace=namespace, db_dict=db_dict, operation="upgrade", ) @@ -1040,7 +1045,6 @@ class K8sHelmBaseConnector(K8sConnector): async def get_service( self, cluster_uuid: str, service_name: str, namespace: str ) -> object: - self.log.debug( "get service, service_name: {}, namespace: {}, cluster_uuid: {}".format( service_name, namespace, cluster_uuid @@ -1123,7 +1127,6 @@ class K8sHelmBaseConnector(K8sConnector): async def get_values_kdu( self, kdu_instance: str, namespace: str, kubeconfig: str ) -> str: - self.log.debug("get kdu_instance values {}".format(kdu_instance)) return await self._exec_get_command( @@ -1155,7 +1158,6 @@ class K8sHelmBaseConnector(K8sConnector): ) async def help_kdu(self, kdu_model: str, repo_url: str = None) -> str: - self.log.debug( "inspect kdu_model {} readme.md from repo: {}".format(kdu_model, repo_url) ) @@ -1165,7 +1167,6 @@ class K8sHelmBaseConnector(K8sConnector): ) async def synchronize_repos(self, cluster_uuid: str): - self.log.debug("synchronize repos for cluster helm-id: {}".format(cluster_uuid)) try: db_repo_ids = self._get_helm_chart_repos_ids(cluster_uuid) @@ -1355,6 +1356,7 @@ class K8sHelmBaseConnector(K8sConnector): atomic, timeout, kubeconfig, + force, ) -> str: """Generates the command to upgrade a Helm Chart release @@ -1368,7 +1370,7 @@ class K8sHelmBaseConnector(K8sConnector): The --wait flag will be set automatically if --atomic is used timeout (float): The time, in seconds, to wait kubeconfig (str): Kubeconfig file path - + force (bool): If set, helm forces resource updates through a replacement strategy. This may recreate pods. Returns: str: command to upgrade a Helm Chart release """ @@ -1537,7 +1539,6 @@ class K8sHelmBaseConnector(K8sConnector): encode_utf8: bool = False, env: dict = None, ) -> (str, int): - command = K8sHelmBaseConnector._remove_multiple_spaces(command) self.log.debug( "Executing async local command: {}, env: {}".format(command, env) @@ -1612,7 +1613,6 @@ class K8sHelmBaseConnector(K8sConnector): encode_utf8: bool = False, env: dict = None, ): - command1 = K8sHelmBaseConnector._remove_multiple_spaces(command1) command2 = K8sHelmBaseConnector._remove_multiple_spaces(command2) command = "{} | {}".format(command1, command2) @@ -1958,7 +1958,6 @@ class K8sHelmBaseConnector(K8sConnector): # params for use in -f file # returns values file option and filename (in order to delete it at the end) def _params_to_file_option(self, cluster_id: str, params: dict) -> (str, str): - if params and len(params) > 0: self._init_paths_env(cluster_name=cluster_id, create_if_not_exist=True) @@ -2091,3 +2090,148 @@ class K8sHelmBaseConnector(K8sConnector): break # it is not necessary to continue the loop if the repo link was found... return repo_url + + async def create_certificate( + self, cluster_uuid, namespace, dns_prefix, name, secret_name, usage + ): + paths, env = self._init_paths_env( + cluster_name=cluster_uuid, create_if_not_exist=True + ) + kubectl = Kubectl(config_file=paths["kube_config"]) + await kubectl.create_certificate( + namespace=namespace, + name=name, + dns_prefix=dns_prefix, + secret_name=secret_name, + usages=[usage], + issuer_name="ca-issuer", + ) + + async def delete_certificate(self, cluster_uuid, namespace, certificate_name): + paths, env = self._init_paths_env( + cluster_name=cluster_uuid, create_if_not_exist=True + ) + kubectl = Kubectl(config_file=paths["kube_config"]) + await kubectl.delete_certificate(namespace, certificate_name) + + async def create_namespace( + self, + namespace, + cluster_uuid, + labels, + ): + """ + Create a namespace in a specific cluster + + :param namespace: Namespace to be created + :param cluster_uuid: K8s cluster uuid used to retrieve kubeconfig + :param labels: Dictionary with labels for the new namespace + :returns: None + """ + paths, env = self._init_paths_env( + cluster_name=cluster_uuid, create_if_not_exist=True + ) + kubectl = Kubectl(config_file=paths["kube_config"]) + await kubectl.create_namespace( + name=namespace, + labels=labels, + ) + + async def delete_namespace( + self, + namespace, + cluster_uuid, + ): + """ + Delete a namespace in a specific cluster + + :param namespace: namespace to be deleted + :param cluster_uuid: K8s cluster uuid used to retrieve kubeconfig + :returns: None + """ + paths, env = self._init_paths_env( + cluster_name=cluster_uuid, create_if_not_exist=True + ) + kubectl = Kubectl(config_file=paths["kube_config"]) + await kubectl.delete_namespace( + name=namespace, + ) + + async def copy_secret_data( + self, + src_secret: str, + dst_secret: str, + cluster_uuid: str, + data_key: str, + src_namespace: str = "osm", + dst_namespace: str = "osm", + ): + """ + Copy a single key and value from an existing secret to a new one + + :param src_secret: name of the existing secret + :param dst_secret: name of the new secret + :param cluster_uuid: K8s cluster uuid used to retrieve kubeconfig + :param data_key: key of the existing secret to be copied + :param src_namespace: Namespace of the existing secret + :param dst_namespace: Namespace of the new secret + :returns: None + """ + paths, env = self._init_paths_env( + cluster_name=cluster_uuid, create_if_not_exist=True + ) + kubectl = Kubectl(config_file=paths["kube_config"]) + secret_data = await kubectl.get_secret_content( + name=src_secret, + namespace=src_namespace, + ) + # Only the corresponding data_key value needs to be copy + data = {data_key: secret_data.get(data_key)} + await kubectl.create_secret( + name=dst_secret, + data=data, + namespace=dst_namespace, + secret_type="Opaque", + ) + + async def setup_default_rbac( + self, + name, + namespace, + cluster_uuid, + api_groups, + resources, + verbs, + service_account, + ): + """ + Create a basic RBAC for a new namespace. + + :param name: name of both Role and Role Binding + :param namespace: K8s namespace + :param cluster_uuid: K8s cluster uuid used to retrieve kubeconfig + :param api_groups: Api groups to be allowed in Policy Rule + :param resources: Resources to be allowed in Policy Rule + :param verbs: Verbs to be allowed in Policy Rule + :param service_account: Service Account name used to bind the Role + :returns: None + """ + paths, env = self._init_paths_env( + cluster_name=cluster_uuid, create_if_not_exist=True + ) + kubectl = Kubectl(config_file=paths["kube_config"]) + await kubectl.create_role( + name=name, + labels={}, + namespace=namespace, + api_groups=api_groups, + resources=resources, + verbs=verbs, + ) + await kubectl.create_role_binding( + name=name, + labels={}, + namespace=namespace, + role_name=name, + sa_name=service_account, + )