From 181eb621c01f9a1cde2a3245b40c809d5803be11 Mon Sep 17 00:00:00 2001
From: fit2bot <68588906+fit2bot@users.noreply.github.com>
Date: Mon, 19 Aug 2024 16:04:00 +0800
Subject: [PATCH] perf: Remove kubernetes tree api (#13995)

* perf: Remove kubernetes tree api

* perf: Update Dockerfile with new base image tag

---------

Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>
---
 apps/assets/utils/__init__.py                 |   1 -
 apps/assets/utils/k8s.py                      | 177 ------------------
 .../user_permission/tree/node_with_asset.py   |  63 +------
 apps/perms/urls/user_permission.py            |   3 -
 poetry.lock                                   |  33 +---
 pyproject.toml                                |   1 -
 6 files changed, 2 insertions(+), 276 deletions(-)
 delete mode 100644 apps/assets/utils/k8s.py

diff --git a/apps/assets/utils/__init__.py b/apps/assets/utils/__init__.py
index 9f588b6d2..97431995d 100644
--- a/apps/assets/utils/__init__.py
+++ b/apps/assets/utils/__init__.py
@@ -1,2 +1 @@
-from .k8s import *
 from .node import *
diff --git a/apps/assets/utils/k8s.py b/apps/assets/utils/k8s.py
deleted file mode 100644
index 8e1069566..000000000
--- a/apps/assets/utils/k8s.py
+++ /dev/null
@@ -1,177 +0,0 @@
-# -*- coding: utf-8 -*-
-from urllib.parse import urlencode, urlparse
-
-from kubernetes import client
-from kubernetes.client import api_client
-from kubernetes.client.api import core_v1_api
-from sshtunnel import SSHTunnelForwarder, BaseSSHTunnelForwarderError
-
-from common.utils import get_logger
-from ..const import CloudTypes, Category
-
-logger = get_logger(__file__)
-
-
-class KubernetesClient:
-    def __init__(self, asset, token):
-        self.url = asset.address
-        self.token = token or ''
-        self.server = self.get_gateway_server(asset)
-
-    @property
-    def api(self):
-        configuration = client.Configuration()
-        scheme = urlparse(self.url).scheme
-        if not self.server:
-            host = self.url
-        else:
-            host = f'{scheme}://127.0.0.1:{self.server.local_bind_port}'
-        configuration.host = host
-        configuration.verify_ssl = False
-        configuration.api_key = {"authorization": "Bearer " + self.token}
-        c = api_client.ApiClient(configuration=configuration)
-        api = core_v1_api.CoreV1Api(c)
-        return api
-
-    def get_namespaces(self):
-        namespaces = []
-        resp = self.api.list_namespace()
-        for ns in resp.items:
-            namespaces.append(ns.metadata.name)
-        return namespaces
-
-    def get_pods(self, namespace):
-        pods = []
-        resp = self.api.list_namespaced_pod(namespace)
-        for pd in resp.items:
-            pods.append(pd.metadata.name)
-        return pods
-
-    def get_containers(self, namespace, pod_name):
-        containers = []
-        resp = self.api.read_namespaced_pod(pod_name, namespace)
-        for container in resp.spec.containers:
-            containers.append(container.name)
-        return containers
-
-    @staticmethod
-    def get_gateway_server(asset):
-        gateway = None
-        if not asset.is_gateway and asset.domain:
-            gateway = asset.domain.select_gateway()
-
-        if not gateway:
-            return
-
-        remote_bind_address = (
-            urlparse(asset.address).hostname,
-            urlparse(asset.address).port or 443
-        )
-        server = SSHTunnelForwarder(
-            (gateway.address, gateway.port),
-            ssh_username=gateway.username,
-            ssh_password=gateway.password,
-            ssh_pkey=gateway.private_key_path,
-            remote_bind_address=remote_bind_address
-        )
-        try:
-            server.start()
-        except BaseSSHTunnelForwarderError:
-            err_msg = 'Gateway is not active: %s' % asset.get('name', '')
-            print('\033[31m %s \033[0m\n' % err_msg)
-        return server
-
-    def run(self, tp, *args):
-        func_name = f'get_{tp}s'
-        data = []
-        if hasattr(self, func_name):
-            try:
-                data = getattr(self, func_name)(*args)
-            except Exception as e:
-                logger.error(f'K8S tree get {tp} error: {e}')
-
-        if self.server:
-            self.server.stop()
-        return data
-
-
-class KubernetesTree:
-    def __init__(self, asset, secret):
-        self.asset = asset
-        self.secret = secret
-
-    def as_asset_tree_node(self):
-        i = str(self.asset.id)
-        name = str(self.asset)
-        node = self.create_tree_node(
-            i, i, name, 'asset', icon='k8s', is_open=True,
-        )
-        return node
-
-    def as_namespace_node(self, name, tp):
-        i = urlencode({'namespace': name})
-        pid = str(self.asset.id)
-        node = self.create_tree_node(i, pid, name, tp, icon='cloud')
-        return node
-
-    def as_pod_tree_node(self, namespace, name, tp):
-        pid = urlencode({'namespace': namespace})
-        i = urlencode({'namespace': namespace, 'pod': name})
-        node = self.create_tree_node(i, pid, name, tp, icon='cloud')
-        return node
-
-    def as_container_tree_node(self, namespace, pod, name, tp):
-        pid = urlencode({'namespace': namespace, 'pod': pod})
-        i = urlencode({'namespace': namespace, 'pod': pod, 'container': name})
-        node = self.create_tree_node(
-            i, pid, name, tp, icon='cloud', is_container=True
-        )
-        return node
-
-    @staticmethod
-    def create_tree_node(id_, pid, name, identity, icon='', is_container=False, is_open=False):
-        node = {
-            'id': id_,
-            'name': name,
-            'title': name,
-            'pId': pid,
-            'isParent': not is_container,
-            'open': is_open,
-            'iconSkin': icon,
-            'meta': {
-                'type': 'k8s',
-                'data': {
-                    'category': Category.CLOUD,
-                    'type': CloudTypes.K8S,
-                    'identity': identity
-                }
-            }
-        }
-        return node
-
-    def async_tree_node(self, namespace, pod):
-        tree = []
-        k8s_client = KubernetesClient(self.asset, self.secret)
-        if pod:
-            tp = 'container'
-            containers = k8s_client.run(
-                tp, namespace, pod
-            )
-            for container in containers:
-                container_node = self.as_container_tree_node(
-                    namespace, pod, container, tp
-                )
-                tree.append(container_node)
-        elif namespace:
-            tp = 'pod'
-            pods = k8s_client.run(tp, namespace)
-            for pod in pods:
-                pod_node = self.as_pod_tree_node(namespace, pod, tp)
-                tree.append(pod_node)
-        else:
-            tp = 'namespace'
-            namespaces = k8s_client.run(tp)
-            for namespace in namespaces:
-                namespace_node = self.as_namespace_node(namespace, tp)
-                tree.append(namespace_node)
-        return tree
diff --git a/apps/perms/api/user_permission/tree/node_with_asset.py b/apps/perms/api/user_permission/tree/node_with_asset.py
index 114c5475e..dbba179f0 100644
--- a/apps/perms/api/user_permission/tree/node_with_asset.py
+++ b/apps/perms/api/user_permission/tree/node_with_asset.py
@@ -1,31 +1,22 @@
 import abc
-from urllib.parse import parse_qsl
 
 from django.conf import settings
 from django.db.models import F, Value, CharField
-from rest_framework.exceptions import PermissionDenied, NotFound
 from rest_framework.generics import ListAPIView
-from rest_framework.generics import get_object_or_404
-from rest_framework.request import Request
 from rest_framework.response import Response
 
-from accounts.const import AliasAccount
 from assets.api import SerializeToTreeNodeMixin
 from assets.models import Asset
-from assets.utils import KubernetesTree
-from authentication.models import ConnectionToken
-from common.exceptions import JMSException
 from common.utils import get_object_or_none, lazyproperty
 from common.utils.common import timeit
 from perms.hands import Node
 from perms.models import PermNode
-from perms.utils import PermAssetDetailUtil, UserPermNodeUtil
 from perms.utils import UserPermAssetUtil
+from perms.utils import UserPermNodeUtil
 from .mixin import RebuildTreeMixin
 from ..mixin import SelfOrPKUserMixin
 
 __all__ = [
-    'UserGrantedK8sAsTreeApi',
     'UserPermedNodesWithAssetsAsTreeApi',
     'UserPermedNodeChildrenWithAssetsAsTreeApi',
     'UserPermedNodeChildrenWithAssetsAsCategoryTreeApi',
@@ -218,55 +209,3 @@ class UserPermedNodeChildrenWithAssetsAsCategoryTreeApi(BaseUserNodeWithAssetAsT
             return self.get_assets()
         else:
             return []
-
-
-class UserGrantedK8sAsTreeApi(SelfOrPKUserMixin, ListAPIView):
-    """ 用户授权的K8s树 """
-
-    def get_token(self):
-        token_id = self.request.query_params.get('token')
-        token = get_object_or_404(ConnectionToken, pk=token_id)
-        if token.is_expired:
-            raise PermissionDenied('Token is expired')
-        token.renewal()
-        return token
-
-    def get_account_secret(self, token: ConnectionToken):
-        util = PermAssetDetailUtil(self.user, token.asset)
-        accounts = util.get_permed_accounts_for_user()
-        account_name = token.account
-
-        if account_name in [AliasAccount.INPUT, AliasAccount.USER]:
-            return token.input_secret
-        else:
-            accounts = filter(lambda x: x.name == account_name, accounts)
-            accounts = list(accounts)
-            if not accounts:
-                raise NotFound('Account is not found')
-            account = accounts[0]
-            return account.secret
-
-    @staticmethod
-    def get_namespace_and_pod(key):
-        namespace_and_pod = dict(parse_qsl(key))
-        pod = namespace_and_pod.get('pod')
-        namespace = namespace_and_pod.get('namespace')
-        return namespace, pod
-
-    def list(self, request: Request, *args, **kwargs):
-        token = self.get_token()
-        asset = token.asset
-        secret = self.get_account_secret(token)
-        key = self.request.query_params.get('key')
-        namespace, pod = self.get_namespace_and_pod(key)
-
-        tree = []
-        k8s_tree_instance = KubernetesTree(asset, secret)
-        if not any([namespace, pod]) and not key:
-            asset_node = k8s_tree_instance.as_asset_tree_node()
-            tree.append(asset_node)
-        try:
-            tree.extend(k8s_tree_instance.async_tree_node(namespace, pod))
-            return Response(data=tree)
-        except Exception as e:
-            raise JMSException(e)
diff --git a/apps/perms/urls/user_permission.py b/apps/perms/urls/user_permission.py
index 0ba99087e..e17ee3218 100644
--- a/apps/perms/urls/user_permission.py
+++ b/apps/perms/urls/user_permission.py
@@ -47,9 +47,6 @@ user_permission_urlpatterns = [
     path('<str:user>/nodes/all-with-assets/tree/',
          api.UserPermedNodesWithAssetsAsTreeApi.as_view(),
          name='user-nodes-with-assets-as-tree'),
-    path('<str:user>/nodes/children-with-k8s/tree/',
-         api.UserGrantedK8sAsTreeApi.as_view(),
-         name='user-nodes-children-with-k8s-as-tree'),
 ]
 
 user_group_permission_urlpatterns = [
diff --git a/poetry.lock b/poetry.lock
index dd4ac20dc..051a97173 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -3500,37 +3500,6 @@ type = "legacy"
 url = "https://mirrors.aliyun.com/pypi/simple"
 reference = "aliyun"
 
-[[package]]
-name = "kubernetes"
-version = "27.2.0"
-description = "Kubernetes python client"
-optional = false
-python-versions = ">=3.6"
-files = [
-    {file = "kubernetes-27.2.0-py2.py3-none-any.whl", hash = "sha256:0f9376329c85cf07615ed6886bf9bf21eb1cbfc05e14ec7b0f74ed8153cd2815"},
-    {file = "kubernetes-27.2.0.tar.gz", hash = "sha256:d479931c6f37561dbfdf28fc5f46384b1cb8b28f9db344ed4a232ce91990825a"},
-]
-
-[package.dependencies]
-certifi = ">=14.05.14"
-google-auth = ">=1.0.1"
-oauthlib = ">=3.2.2"
-python-dateutil = ">=2.5.3"
-pyyaml = ">=5.4.1"
-requests = "*"
-requests-oauthlib = "*"
-six = ">=1.9.0"
-urllib3 = ">=1.24.2"
-websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.dev0 || >=0.43.dev0"
-
-[package.extras]
-adal = ["adal (>=1.0.2)"]
-
-[package.source]
-type = "legacy"
-url = "https://mirrors.aliyun.com/pypi/simple"
-reference = "aliyun"
-
 [[package]]
 name = "ldap3"
 version = "2.9.1"
@@ -7701,4 +7670,4 @@ reference = "aliyun"
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.11"
-content-hash = "2dc429e66e78ab1e264e26da60df6a67cb8d5e501d80297332d673646bc0cf13"
+content-hash = "9acfafd75bf7dbb7e0dffb54b7f11f6b09aa4ceff769d193a3906d03ae796ccc"
diff --git a/pyproject.toml b/pyproject.toml
index f0ad19047..9b2047f6c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -126,7 +126,6 @@ django-auth-ldap = "4.4.0"
 boto3 = "1.28.9"
 botocore = "1.31.9"
 s3transfer = "0.6.1"
-kubernetes = "27.2.0"
 mysqlclient = "2.2.4"
 pymssql = "2.2.8"
 django-redis = "5.3.0"