From aa2907e58e7039a30bca6f932b6779c98d967dba Mon Sep 17 00:00:00 2001 From: bellkeyang Date: Mon, 6 Sep 2021 14:37:05 +0800 Subject: [PATCH 01/10] feat: write data to cluster manager --- bcs-app/backend/components/bcs_api.py | 284 +++++- bcs-app/backend/components/paas_cc.py | 41 +- .../backend/container_service/clusters/cm.py | 120 +++ .../container_service/clusters/constants.py | 20 +- .../container_service/projects/auditor.py | 21 + .../backend/container_service/projects/cm.py | 89 ++ .../container_service/projects/views.py | 29 +- bcs-app/backend/settings/ce/base.py | 2 + .../backend/tests/components/test_bcs_api.py | 138 ++- .../container_service/clusters/test_cm.py | 57 ++ .../container_service/projects/test_cm.py | 43 + .../tests/testing_utils/mocks/bcs_api.py | 881 +++++++++++++++++- .../tests/testing_utils/mocks/bk_cc.py | 160 +++- 13 files changed, 1828 insertions(+), 57 deletions(-) create mode 100644 bcs-app/backend/container_service/clusters/cm.py create mode 100644 bcs-app/backend/container_service/projects/auditor.py create mode 100644 bcs-app/backend/container_service/projects/cm.py create mode 100644 bcs-app/backend/tests/container_service/clusters/test_cm.py create mode 100644 bcs-app/backend/tests/container_service/projects/test_cm.py diff --git a/bcs-app/backend/components/bcs_api.py b/bcs-app/backend/components/bcs_api.py index 4c30ed28a..4d42ef2aa 100644 --- a/bcs-app/backend/components/bcs_api.py +++ b/bcs-app/backend/components/bcs_api.py @@ -5,20 +5,25 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://opensource.org/licenses/MIT - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ -from typing import Dict +from dataclasses import asdict, dataclass, field +from typing import Dict, List from django.conf import settings from requests import PreparedRequest from requests.auth import AuthBase -from .base import BaseHttpClient, BkApiClient, ComponentAuth, update_url_parameters +from .base import BaseHttpClient, BkApiClient, ComponentAuth, response_handler, update_url_parameters + +# bcs api的版本 +bcs_version = "v4" + +record_not_exist_code = 1405420 +record_exist_code = 1405405 class BcsApiConfig: @@ -32,6 +37,22 @@ def __init__(self, host: str): self.get_cluster_credentials_url = f"{host}/{{env_name}}/rest/clusters/{{bcs_cluster_id}}/client_credentials" +class BcsApiGatewayConfig: + def __init__(self, host: str, env: str): + self.host = host + + # cluster manager相关接口url + self.query_project_url = f"{host}/{env}/{bcs_version}/clustermanager/v1/project/{{project_id}}" + self.create_project_url = f"{host}/{env}/{bcs_version}/clustermanager/v1/project/{{project_id}}" + self.update_project_url = f"{host}/{env}/{bcs_version}/clustermanager/v1/project/{{project_id}}" + self.add_cluster_url = f"{host}/{env}/{bcs_version}/clustermanager/v1/cluster/{{cluster_id}}" + self.delete_cluster_url = f"{host}/{env}/{bcs_version}/clustermanager/v1/cluster/{{cluster_id}}" + self.update_cluster_url = f"{host}/{env}/{bcs_version}/clustermanager/v1/cluster/{{cluster_id}}" + self.add_nodes_url = f"{host}/{env}/{bcs_version}/clustermanager/v1/cluster/{{cluster_id}}/node" + self.delete_nodes_url = f"{host}/{env}/{bcs_version}/clustermanager/v1/cluster/{{cluster_id}}/node" + self.query_task_url = f"{host}/{env}/{bcs_version}/clustermanager/v1/task/{{task_id}}" + + class BcsApiAuth(AuthBase): """用于调用 bcs-api 系统的鉴权对象""" @@ -49,14 +70,149 @@ def __call__(self, r: PreparedRequest): return r +@dataclass +class ProjectReservedConfig: + bgID: str = "" + bgName: str = "" + deptID: str = "" + deptName: str = "" + centerID: str = "" + centerName: str = "" + isSecret: bool = False + deployType: int = 2 # 业务部署类型,1:物理机部署,2:容器部署 + isOffline: bool = False # 项目是否已经离线,默认False + useBKRes: bool = False # 是否使用蓝鲸提供的资源池,主要用于资源计费,默认False + projectType: int = 1 # 1:platform,2:business + + +@dataclass +class ProjectBasicConfig: + """项目的基本配置 + projectID: 项目ID,长度为32位字符串 + name: 项目名称 + englishName: 项目英文缩写,长度不能超过32字符 + kind: 项目中集群类型,支持k8s/mesos + businessID: 项目绑定的BK CC的业务ID + credentials: 记录的账户信息 + description: 项目的描述信息,默认为空 + """ + + projectID: str + name: str + englishName: str + kind: str + businessID: str + description: str + credentials: Dict = field(default_factory=dict) + + +@dataclass +class ProjectConfig: + """项目配置信息""" + + creator: str + basic_config: ProjectBasicConfig + reserved_config: ProjectReservedConfig + + +@dataclass +class UpdatedProjectConfig: + """更新项目配置信息""" + + projectID: str + updater: str + name: str + kind: str + businessID: str + + +@dataclass +class CloudClusterConfig: + """集群初始化需要的基本配置 + region: 区域 + manageType: 集群管理类型,公有云时生效,MANAGED_CLUSTER(云上托管集群),INDEPENDENT_CLUSTER(独立集群,自行维护) + master: 集群master + vpcID: vpc ID + cloudID: 所属云ID + nodes: 集群的节点 + networkSettings: 网络配置 + clusterBasicSettings: 集群的基本配置 + clusterAdvanceSettings: 集群的高级配置 + nodeSettings: 节点配置信息 + systemReinstall: 是否重装master节点的系统,机器被托管情况下有效 + initLoginPassword: 重装master节点的系统时,初始化password,机器被托管情况下有效 + status: 状态 + """ + + region: str + master: List + manageType: str = "INDEPENDENT_CLUSTER" + vpcID: str = "" + cloudID: str = "" + nodes: List = field(default_factory=list) + networkSettings: Dict = field(default_factory=dict) + clusterBasicSettings: Dict = field(default_factory=dict) + clusterAdvanceSettings: Dict = field(default_factory=dict) + nodeSettings: Dict = field(default_factory=dict) + systemReinstall: bool = False + initLoginPassword: str = "" + status: str = "" + + +@dataclass +class BcsClusterConfig: + """集群初始化时,BCS需要的基本配置""" + + projectID: str + businessID: str + clusterID: str + clusterName: str + provider: str + environment: str # 集群环境,如prod、debug、test + engineType: str # 引擎类型,如k8s、mesos + clusterType: str = "single" # 集群类型, 例如[federation, single], federation表示为联邦集群,single表示独立集群,默认为single + isExclusive: bool = False # 是否为独占集群 + federationClusterID: str = "" # 联邦集群ID + labels: Dict = field(default_factory=dict) + onlyCreateInfo: bool = False + bcsAddons: Dict = field(default_factory=dict) + extraAddons: Dict = field(default_factory=dict) + + +@dataclass +class ClusterConfig: + """集群配置""" + + creator: str + bcs_cluster_config: BcsClusterConfig + cloud_cluster_config: CloudClusterConfig + + +@dataclass +class UpdatedClusterConfig: + """更新的集群配置选项""" + + projectID: str + clusterID: str + updater: str + status: str + clusterName: str = "" + labels: Dict = field(default_factory=dict) + + +@dataclass +class NodeConfig: + nodes: List[str] + nodeGroupID: str = "" + onlyCreateInfo: bool = False + initLoginPassword: str = "" + + class BcsApiClient(BkApiClient): """访问 BCS API 服务的 Client 对象 - :param auth: 包含校验信息的对象 - API 方法常用请求参数说明 === - :param env_name: 集群环境,比如 stag/prod :param project_id: 项目 ID :param cluster_id: 集群 ID @@ -66,10 +222,11 @@ class BcsApiClient(BkApiClient): def __init__(self, auth: ComponentAuth): self._config = BcsApiConfig(host=settings.BCS_API_PRE_URL) self._client = BaseHttpClient(BcsApiAuth(auth.access_token)) + self._apigw_config = BcsApiGatewayConfig(host=settings.BCS_API_GW_PRE_URL, env=settings.BCS_API_GW_ENV) + self._apigw_client = BaseHttpClient(BcsApiAuth(auth.access_token)) def query_cluster_id(self, env_name: str, project_id: str, cluster_id: str) -> str: """查询集群在 BCS-Api 中的 ID - :returns: 集群 ID 字符串 """ url = self._config.query_cluster_id_url.format(env_name=env_name) @@ -81,8 +238,117 @@ def query_cluster_id(self, env_name: str, project_id: str, cluster_id: str) -> s def get_cluster_credentials(self, env_name: str, bcs_cluster_id: str) -> Dict: """ 获取访问集群 apiserver 所需的鉴权信息,比如证书、user_token、server_address_path 等 - :returns: 包含集群鉴权信息的字典 """ url = self._config.get_cluster_credentials_url.format(env_name=env_name, bcs_cluster_id=bcs_cluster_id) return self._client.request_json('GET', url, raise_for_status=False) + + @response_handler() + def query_project(self, project_id: str) -> Dict: + """查询项目信息 + :param project_id: 项目ID + :returns: 返回项目信息 + """ + url = self._apigw_config.query_project_url.format(project_id=project_id) + return self._apigw_client.request_json("GET", url) + + def create_project(self, project_config: ProjectConfig) -> Dict: + """创建项目 + :param project_config: 项目信息,包含项目的基本信息和项目的保留字段信息 + :returns: 返回项目信息 + """ + # 组装参数 + project_config_data = asdict(project_config.basic_config) + project_config_data.update(asdict(project_config.reserved_config), creator=project_config.creator) + # 下发配置 + url = self._apigw_config.create_project_url.format(project_id=project_config_data["projectID"]) + return self._apigw_client.request_json("POST", url, json=project_config_data) + + def update_project(self, project_config: UpdatedProjectConfig) -> Dict: + """更新项目 + :param project_config: 更新的项目信息 + :returns: 返回更新的项目信息 + """ + # 组装参数 + project_config_data = asdict(project_config) + # 下发配置 + url = self._apigw_config.create_project_url.format(project_id=project_config_data["projectID"]) + return self._apigw_client.request_json("PUT", url, json=project_config_data) + + def add_cluster(self, cluster_config: ClusterConfig) -> Dict: + """添加集群 + :param creator: 创建者 + :param cluster_config: 集群初始化的配置,包含bcs平台需要的信息和集群初始化需要的信息 + :returns: 返回初始化的集群的信息,任务信息,格式: {"data": {}, "task": {}} + """ + # 组装参数 + cluster_config_data = asdict(cluster_config.cloud_cluster_config) + cluster_config_data.update(asdict(cluster_config.bcs_cluster_config), creator=cluster_config.creator) + # 下发初始化配置 + url = self._apigw_config.add_cluster_url.format(cluster_id=cluster_config_data["clusterID"]) + return self._apigw_client.request_json("POST", url, json=cluster_config_data) + + @response_handler() + def update_cluster(self, cluster_config: UpdatedClusterConfig) -> Dict: + """更新集群 + :param cluster_config: 更新集群的配置 + :returns: 返回集群的配置 + """ + # 组装参数,并去掉为None的属性 + cluster_config_data = {k: v for k, v in asdict(cluster_config).items() if v is not None} + # 下发更新的配置 + url = self._apigw_config.update_cluster_url.format(cluster_id=cluster_config_data["clusterID"]) + return self._apigw_client.request_json("PUT", url, json=cluster_config_data) + + def delete_cluster( + self, + cluster_id: str, + is_force: bool = False, + is_clean_resource: bool = True, + only_delete_info: bool = False, + ) -> Dict: + """删除集群 + :param cluster_id: 集群ID + :param is_force: 是否强制删除,默认为False + :param is_clean_resource: 强制删除有效时,是否清理机器上部署的资源 + :param only_delete_info: 是否仅删除记录的信息,默认为False + :returns: 返回删除的集群信息,任务信息 + """ + url = self._apigw_config.delete_cluster_url.format(cluster_id=cluster_id) + params = {"isForced": is_force, "resourceClean": is_clean_resource, "onlyDeleteInfo": only_delete_info} + return self._apigw_client.request_json("DELETE", url, params=params) + + @response_handler() + def add_nodes(self, cluster_id: str, node_config: NodeConfig) -> Dict: + """添加节点 + :param cluster_id: 集群ID + :param node_config: 节点配置 + :returns: 返回节点的数据,包含任务ID + """ + url = self._apigw_config.add_nodes_url.format(cluster_id=cluster_id) + data = asdict(node_config) + data["clusterID"] = cluster_id + return self._apigw_client.request_json("POST", url, json=data) + + @response_handler() + def delete_nodes( + self, cluster_id: str, nodes: List[str], delete_mode: str = "RETAIN", is_force: bool = False + ) -> Dict: + """删除节点 + :param cluster_id: 集群ID + :param nodes: 节点列表 + :param delete_mode: 删除模式,RETAIN(移除集群,但是保留主机),TERMINATE(只支持按量计费的机器) + :param is_force: 是否强制删除 + :returns: 返回删除的节点信息,包含任务ID + """ + url = self._apigw_config.delete_nodes_url.format(cluster_id=cluster_id) + data = {"clusterID": cluster_id, "nodes": nodes, "deleteMode": delete_mode, "isForce": is_force} + return self._apigw_client.request_json("DELETE", url, json=data) + + def query_task(self, task_id: str) -> Dict: + """查询任务状态 + :param task_id: 任务ID + :returns: 返回任务的执行详情 + """ + url = self._apigw_config.query_task_url.format(task_id=task_id) + return self._apigw_client.request_json("GET", url) diff --git a/bcs-app/backend/components/paas_cc.py b/bcs-app/backend/components/paas_cc.py index f8afcc170..0412796c4 100644 --- a/bcs-app/backend/components/paas_cc.py +++ b/bcs-app/backend/components/paas_cc.py @@ -5,9 +5,7 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://opensource.org/licenses/MIT - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. @@ -521,6 +519,9 @@ def __init__(self, host: str): self.update_node_list_url = f"{host}/projects/{{project_id}}/clusters/{{cluster_id}}/nodes/" self.get_cluster_namespace_list_url = f"{host}/projects/{{project_id}}/clusters/{{cluster_id}}/namespaces/" self.get_node_list_url = f"{host}/projects/{{project_id}}/nodes/" + self.update_project_url = f"{host}/projects/{{project_id}}/" + self.add_cluster_url = f"{host}/projects/{{project_id}}/clusters/" + self.add_nodes_url = f"{host}/projects/{{project_id}}/clusters/{{cluster_id}}/nodes/" @dataclass @@ -531,7 +532,6 @@ class UpdateNodesData: class PaaSCCClient(BkApiClient): """访问 PaaSCC 服务的 Client 对象 - :param auth: 包含校验信息的对象 """ @@ -553,7 +553,6 @@ def get_project(self, project_id: str) -> Dict: @response_handler() def update_cluster(self, project_id: str, cluster_id: str, data: Dict) -> Dict: """更新集群信息 - :param project_id: 项目32为长度 ID :param cluster_id: 集群ID :param data: 更新的集群属性,包含status,名称、描述等 @@ -564,7 +563,6 @@ def update_cluster(self, project_id: str, cluster_id: str, data: Dict) -> Dict: @response_handler() def delete_cluster(self, project_id: str, cluster_id: str): """删除集群 - :param project_id: 项目32为长度 ID :param cluster_id: 集群ID """ @@ -574,7 +572,6 @@ def delete_cluster(self, project_id: str, cluster_id: str): @response_handler() def update_node_list(self, project_id: str, cluster_id: str, nodes: List[UpdateNodesData]) -> List: """更新节点信息 - :param project_id: 项目32为长度 ID :param cluster_id: 集群ID :param nodes: 更新的节点属性,包含IP和状态 @@ -594,7 +591,6 @@ def get_cluster_namespace_list( desire_all_data: Union[bool, int] = None, ) -> Dict[str, Union[int, List[Dict]]]: """获取集群下命名空间列表 - :param project_id: 项目ID :param cluster_id: 集群ID :param limit: 每页的数量 @@ -633,6 +629,37 @@ def get_node_list(self, project_id: str, cluster_id: str, params: Dict = None) - req_params.setdefault("desire_all_data", 1) return self._client.request_json("GET", url, params=req_params) + @response_handler() + def update_project(self, project_id: str, data: Dict) -> Dict: + """更新项目信息 + :param project_id: 项目ID + :param data: 要更新的项目信息 + :returns: 返回更新后的项目信息 + """ + url = self._config.update_project_url.format(project_id=project_id) + return self._client.request_json("PUT", url, json=data) + + @response_handler() + def create_cluster(self, project_id: str, data: Dict) -> Dict: + """创建集群信息 + :param project_id: 项目ID + :param data: 集群信息 + :returns: 返回创建的集群信息 + """ + url = self._config.add_cluster_url.format(project_id=project_id) + return self._client.request_json("POST", url, json=data) + + @response_handler() + def add_nodes(self, project_id: str, cluster_id: str, data: Dict) -> List: + """添加节点信息 + :param project_id: 项目ID + :param cluster_id: 集群ID + :param data: 添加的节点信息 + :returns: 返回节点数据 + """ + url = self._config.add_nodes_url.format(project_id=project_id, cluster_id=cluster_id) + return self._client.request_json("PATCH", url, json=data) + try: from .paas_cc_ext import get_auth_project # noqa diff --git a/bcs-app/backend/container_service/clusters/cm.py b/bcs-app/backend/container_service/clusters/cm.py new file mode 100644 index 000000000..fffcdb837 --- /dev/null +++ b/bcs-app/backend/container_service/clusters/cm.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import logging +from typing import Dict, List + +from django.conf import settings + +from backend.components import base as comp_base +from backend.components import bcs_api, paas_cc +from backend.container_service.clusters.models import CommonStatus + +from . import constants + +logger = logging.getLogger(__name__) + + +def create_cluster(access_token: str, project_id: str, cc_app_id: int, data: Dict) -> Dict: + """创建集群信息""" + bcs_cc_client = paas_cc.PaaSCCClient(comp_base.ComponentAuth(access_token)) + cluster_env = settings.CLUSTER_ENV.get(data["environment"]) + data["environment"] = cluster_env + cluster_data = bcs_cc_client.create_cluster(project_id, data) + + # 写入数据到clustermanager + try: + bcs_cluster_config = bcs_api.BcsClusterConfig( + projectID=project_id, + businessID=str(cc_app_id), + clusterID=cluster_data["cluster_id"], + clusterName=data["name"], + provider="bcs", + environment=cluster_env, + engineType=data["coes"], + onlyCreateInfo=True, + ) + # NOTE: 切换流程后需要适配集群的配置 + cloud_cluster_config = bcs_api.CloudClusterConfig( + region=data.get("area_id"), + master=data.get("master_ips") or [], + vpcID=data.get("vpc_id"), + clusterBasicSettings={"version": data.get("version", "")}, + clusterAdvanceSettings={ + "containerRuntime": constants.ContainerRuntime.Docker, + "IPVS": True if data.get("kube_proxy_mode") == constants.KubeProxy.IPVS else False, + }, + ) + cluster_config = bcs_api.ClusterConfig( + creator=data["creator"], bcs_cluster_config=bcs_cluster_config, cloud_cluster_config=cloud_cluster_config + ) + client = bcs_api.BcsApiClient(comp_base.ComponentAuth(access_token)) + client.add_cluster(cluster_config) + except Exception as e: + logger.error("add clustermanager cluster failed, %s", e) + return cluster_data + + +def update_cluster(access_token: str, project_id: str, cluster_id: str, data: Dict) -> Dict: + """更新集群""" + bcs_cc_client = paas_cc.PaaSCCClient(comp_base.ComponentAuth(access_token)) + cluster_data = bcs_cc_client.update_cluster(project_id, cluster_id, data) + try: + client = bcs_api.BcsApiClient(comp_base.ComponentAuth(access_token)) + cluster_config = bcs_api.UpdatedClusterConfig( + projectID=project_id, + clusterID=cluster_id, + updater=data.get("updater"), + clusterName=data.get("name"), + status=constants.ClusterStatus.RUNNING, + ) + client.update_cluster(cluster_config) + except Exception as e: + logger.error("update clustermanager cluster failed, %s", e) + + return cluster_data + + +def delete_cluster(access_token: str, project_id: str, cluster_id: str): + """删除集群""" + bcs_cc_client = paas_cc.PaaSCCClient(comp_base.ComponentAuth(access_token)) + cluster_data = bcs_cc_client.update_cluster(project_id, cluster_id, {"status": CommonStatus.Removing}) + try: + client = bcs_api.BcsApiClient(comp_base.ComponentAuth(access_token)) + client.delete_cluster(cluster_id=cluster_id, only_delete_info=True) + except Exception as e: + logger.error("delete clustermanager cluster failed, %s", e) + return cluster_data + + +def add_nodes(access_token: str, project_id: str, cluster_id: str, data: Dict): + """添加节点""" + bcs_cc_client = paas_cc.PaaSCCClient(comp_base.ComponentAuth(access_token)) + bcs_cc_client.add_nodes(project_id, cluster_id, data) + try: + client = bcs_api.BcsApiClient(comp_base.ComponentAuth(access_token)) + node_config = bcs_api.NodeConfig(nodes=[node["inner_ip"] for node in data["objects"]], onlyCreateInfo=True) + client.add_nodes(cluster_id, node_config) + except Exception as e: + logger.error("add clustermanager nodes failed, %s", e) + + +def delete_nodes(access_token: str, project_id: str, cluster_id: str, node_ips: List[str]): + """删除节点""" + bcs_cc_client = paas_cc.PaaSCCClient(comp_base.ComponentAuth(access_token)) + data = [paas_cc.UpdateNodesData(inner_ip=ip, status=CommonStatus.Removing) for ip in node_ips] + bcs_cc_client.update_node_list(project_id, cluster_id, data) + try: + client = bcs_api.BcsApiClient(comp_base.ComponentAuth(access_token)) + client.delete_nodes(cluster_id, node_ips) + except Exception as e: + logger.error("update clustermanager nodes failed, %s", e) diff --git a/bcs-app/backend/container_service/clusters/constants.py b/bcs-app/backend/container_service/clusters/constants.py index 2a590e281..26bb012c9 100644 --- a/bcs-app/backend/container_service/clusters/constants.py +++ b/bcs-app/backend/container_service/clusters/constants.py @@ -5,9 +5,7 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://opensource.org/licenses/MIT - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. @@ -198,3 +196,21 @@ class KubeProxy(str, StructuredEnum): # k8s cluster master role # 参考rancher中定义nodeRoleMaster="node-role.kubernetes.io/master" K8S_NODE_ROLE_MASTER = "node-role.kubernetes.io/master" + + +class ContainerRuntime(str, StructuredEnum): + """容器运行时""" + + Docker = "docker" + Containerd = "containerd" + + +class ClusterStatus(str, StructuredEnum): + """集群状态""" + + CREATING = EnumField("CREATING", label="创建中") + RUNNING = EnumField("RUNNING", label="运行中,标识集群正常") + DELETING = EnumField("DELETING", label="删除中") + FAILURE = EnumField("FAILURE", label="失败") + INITIALIZATION = EnumField("initializing", label="初始化中") + DELETED = EnumField("DELETED", label="已删除") diff --git a/bcs-app/backend/container_service/projects/auditor.py b/bcs-app/backend/container_service/projects/auditor.py new file mode 100644 index 000000000..92a3c0594 --- /dev/null +++ b/bcs-app/backend/container_service/projects/auditor.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from backend.bcs_web.audit_log.audit.auditors import Auditor +from backend.bcs_web.audit_log.audit.context import AuditContext +from backend.bcs_web.audit_log.constants import ResourceType + + +class ProjectAuditor(Auditor): + def __init__(self, audit_ctx: AuditContext): + super().__init__(audit_ctx) + self.audit_ctx.resource_type = ResourceType.Project diff --git a/bcs-app/backend/container_service/projects/cm.py b/bcs-app/backend/container_service/projects/cm.py new file mode 100644 index 000000000..1b533b610 --- /dev/null +++ b/bcs-app/backend/container_service/projects/cm.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import logging +from typing import Dict + +from backend.components import base as comp_base +from backend.components import bcs_api, paas_cc +from backend.utils.error_codes import error_codes + +logger = logging.getLogger(__name__) + + +def create_project(client: bcs_api.BcsApiClient, project_config: bcs_api.ProjectConfig): + """创建cluster manager中项目信息""" + try: + resp = client.create_project(project_config) + except Exception as e: + raise error_codes.APIError(f"create cm project error, {e}") + return resp + + +def update_project(client: bcs_api.BcsApiClient, project_config: bcs_api.UpdatedProjectConfig): + """更新cluster manager中项目信息""" + try: + resp = client.update_project(project_config) + except Exception as e: + raise error_codes.APIError(f"update cm project error, {e}") + return resp + + +def update_or_create_project(access_token: str, project_id: str, data: Dict) -> Dict: + """创建cluster manager所属的项目信息 + NOTE: 当调用接口出现异常后,现阶段不抛出异常;待下线bcs cc模块时,调整为必选步骤 + """ + # TODO: bcs cc服务下掉后,删除对应的接口 + bcs_cc_client = paas_cc.PaaSCCClient(comp_base.ComponentAuth(access_token)) + project_data = bcs_cc_client.update_project(project_id, data) + client = bcs_api.BcsApiClient(comp_base.ComponentAuth(access_token)) + try: + updated_project_config = bcs_api.UpdatedProjectConfig( + projectID=project_id, + updater=data["updator"], + name=project_data["project_name"], + kind="k8s", + businessID=project_data["cc_app_id"], + ) + resp = update_project(client, updated_project_config) + # 根据code判断项目是否存在,当项目不存在时,调用创建项目接口 + if resp.get("code") == bcs_api.record_not_exist_code: + # 组装写入cluster manager的数据 + reserved_config = bcs_api.ProjectReservedConfig( + bgID=project_data["bg_id"], + bgName=project_data["bg_name"], + deptName=project_data["dept_name"], + deptID=project_data["dept_id"], + centerID=project_data["center_id"], + centerName=project_data["center_name"], + isSecret=project_data["is_secrecy"], + isOffline=project_data["is_offlined"], + useBKRes=project_data["use_bk"], + projectType=project_data["project_type"], + ) + basic_config = bcs_api.ProjectBasicConfig( + projectID=project_id, + name=project_data["project_name"], + englishName=project_data["english_name"], + kind="k8s", + businessID=project_data["cc_app_id"], + description=project_data["description"], + ) + project_config = bcs_api.ProjectConfig( + creator=project_data["creator"], basic_config=basic_config, reserved_config=reserved_config + ) + # 当提示项目不存在时,需要添加项目 + create_project(client, project_config) + except Exception as e: + logger.error("update or create clustermanager project failed, %s", e) + + return project_data diff --git a/bcs-app/backend/container_service/projects/views.py b/bcs-app/backend/container_service/projects/views.py index 210d28f0c..fb3ce82ac 100644 --- a/bcs-app/backend/container_service/projects/views.py +++ b/bcs-app/backend/container_service/projects/views.py @@ -5,28 +5,30 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://opensource.org/licenses/MIT - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ import json import logging +from typing import Dict from django.conf import settings from django.utils.translation import ugettext_lazy as _ from rest_framework import permissions, viewsets from rest_framework.renderers import BrowsableAPIRenderer +from rest_framework.request import Request from rest_framework.response import Response from rest_framework.views import APIView from backend.bcs_web.audit_log import client +from backend.bcs_web.audit_log.audit.decorators import log_audit_on_view +from backend.bcs_web.audit_log.constants import ActivityType from backend.bcs_web.constants import bcs_project_cache_key from backend.bcs_web.iam.permissions import ProjectPermission from backend.bcs_web.viewsets import SystemViewSet -from backend.components import paas_cc +from backend.components import bcs_api, paas_cc from backend.container_service.projects import base as Project from backend.container_service.projects.utils import ( get_app_by_user_role, @@ -40,7 +42,8 @@ from backend.utils.func_controller import get_func_controller from backend.utils.renderers import BKAPIRenderer -from . import serializers +from . import cm, serializers +from .auditor import ProjectAuditor from .cmdb import list_biz_maintainers logger = logging.getLogger(__name__) @@ -153,6 +156,7 @@ def invalid_project_cache(self, project_id): # NOTE: 后续permission统一后,可以删除下面的缓存标识 region.delete(f"BK_DEVOPS_BCS:HAS_BCS_SERVICE:{project_id}") + @log_audit_on_view(ProjectAuditor, activity_type=ActivityType.Modify) def update(self, request, project_id): """更新项目信息""" if not self.can_edit(request, project_id): @@ -161,21 +165,8 @@ def update(self, request, project_id): access_token = request.user.token.access_token data["updator"] = request.user.username - # 添加操作日志 - ual_client = client.UserActivityLogClient( - project_id=project_id, - user=request.user.username, - resource_type="project", - resource=request.project.project_name, - resource_id=project_id, - description="{}: {}".format(_("更新项目"), request.project.project_name), - ) - resp = paas_cc.update_project_new(access_token, project_id, data) - if resp.get("code") != ErrorCode.NoError: - ual_client.log_modify(activity_status="failed") - raise error_codes.APIError(_("更新项目信息失败,错误详情: {}").format(resp.get("message"))) - ual_client.log_modify(activity_status="succeed") - project_data = resp.get("data") + # 统一更新或创建项目部分,方便bcs cc模块移除后的处理 + project_data = cm.update_or_create_project(access_token, project_id, data) if project_data: project_data["created_at"], project_data["updated_at"] = self.normalize_create_update_time( project_data["created_at"], project_data["updated_at"] diff --git a/bcs-app/backend/settings/ce/base.py b/bcs-app/backend/settings/ce/base.py index 172dbc1d0..f8c893eef 100644 --- a/bcs-app/backend/settings/ce/base.py +++ b/bcs-app/backend/settings/ce/base.py @@ -236,6 +236,8 @@ def configure_workers(*args, **kwargs): BCS_CLUSTER_ENV_AND_HTTPS_SERVER_HOST = {"prod": os.environ.get("BKAPP_BCS_API_DOMAIN")} # BCS API PRE URL BCS_API_PRE_URL = f"{APIGW_HOST}/api/apigw/bcs_api" +BCS_API_GW_PRE_URL = f"{APIGW_HOST}/api/apigw/bcs-api-gateway" +BCS_API_GW_ENV = "prod" BK_SSM_HOST = os.environ.get("BKAPP_SSM_HOST") diff --git a/bcs-app/backend/tests/components/test_bcs_api.py b/bcs-app/backend/tests/components/test_bcs_api.py index 6eebf7d9c..72a2bdb3b 100644 --- a/bcs-app/backend/tests/components/test_bcs_api.py +++ b/bcs-app/backend/tests/components/test_bcs_api.py @@ -5,9 +5,7 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://opensource.org/licenses/MIT - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. @@ -15,11 +13,15 @@ import pytest from requests_mock import ANY +from backend.components import bcs_api from backend.components.base import ComponentAuth from backend.components.bcs_api import BcsApiClient BCS_AUTH_TOKEN = 'example-auth-token' +fake_ip = "127.0.0.1" +success_code = 0 + @pytest.fixture(autouse=True) def setup_token(settings): @@ -45,3 +47,135 @@ def test_get_cluster_credentials(self, requests_mock): client = BcsApiClient(ComponentAuth('fake_token')) resp = client.get_cluster_credentials('stag', 'fake-bcs-cluster-foo') assert resp == {'name': 'foo'} + + def test_query_project(self, project_id, request_user, requests_mock): + expected_data = {"project_id": project_id} + requests_mock.get(ANY, json={"code": success_code, "data": expected_data}) + + client = BcsApiClient(ComponentAuth(request_user.token.access_token)) + resp = client.query_project(project_id) + assert resp == expected_data + + def test_create_project(self, project_id, request_user, random_name, requests_mock): + requests_mock.post(ANY, json={"code": success_code, "message": "success"}) + + client = BcsApiClient(ComponentAuth(request_user.token.access_token)) + basic_config = bcs_api.ProjectBasicConfig( + projectID=project_id, + name=random_name, + englishName=random_name, + kind="k8s", + businessID=1, + description="", + credentials={}, + ) + reserved_config = bcs_api.ProjectReservedConfig( + bgID="", + bgName="", + deptID="", + deptName="", + centerID="", + centerName="", + isSecret=False, + deployType=2, + isOffline=False, + useBKRes=False, + projectType=1, + ) + project_config = bcs_api.ProjectConfig( + creator=request_user.username, basic_config=basic_config, reserved_config=reserved_config + ) + resp = client.create_project(project_config) + assert resp["code"] == success_code + + def test_update_project(self, project_id, request_user, random_name, requests_mock): + expected_data = {"project_id": project_id} + requests_mock.put(ANY, json={"code": success_code, "data": expected_data}) + + client = BcsApiClient(ComponentAuth(request_user.token.access_token)) + project_config = bcs_api.UpdatedProjectConfig( + projectID=project_id, + updater=request_user.username, + name=random_name, + kind="k8s", + businessID=1, + ) + resp = client.update_project(project_config) + assert resp["code"] == success_code + + def test_add_cluster(self, project_id, cluster_id, request_user, random_name, requests_mock): + expected_data = {"cluster_id": cluster_id} + expected_task = {"taskID": random_name} + requests_mock.post(ANY, json={"code": 0, "data": expected_data, "task": expected_task}) + + client = BcsApiClient(ComponentAuth(request_user.token.access_token)) + cloud_cluster_config = bcs_api.CloudClusterConfig( + region=random_name, + manageType="INDEPENDENT_CLUSTER", + master=["fake_ip"], + vpcID=random_name, + cloudID=1, + nodes=[], + networkSettings={}, + clusterBasicSettings={}, + clusterAdvanceSettings={}, + nodeSettings={}, + systemReinstall=False, + initLoginPassword="", + status="", + ) + bcs_cluster_config = bcs_api.BcsClusterConfig( + projectID=project_id, + businessID=1, + clusterID=cluster_id, + clusterName=random_name, + provider="", + environment="test", + engineType="k8s", + isExclusive=False, + clusterType="k8s", + federationClusterID="", + labels={}, + onlyCreateInfo=True, + bcsAddons={}, + extraAddons={}, + ) + cluster_config = bcs_api.ClusterConfig( + creator=request_user.username, + cloud_cluster_config=cloud_cluster_config, + bcs_cluster_config=bcs_cluster_config, + ) + resp = client.add_cluster(cluster_config) + assert resp["task"] == expected_task + assert resp["data"] == expected_data + + def test_update_cluster(self, project_id, cluster_id, request_user, random_name, requests_mock): + expected_data = {"cluster_id": cluster_id} + requests_mock.put(ANY, json={"code": 0, "data": expected_data}) + + client = BcsApiClient(ComponentAuth(request_user.token.access_token)) + cluster_config = bcs_api.UpdatedClusterConfig( + projectID=project_id, + clusterID=cluster_id, + updater=request_user.username, + clusterName="test", + status="RUNNING", + ) + resp = client.update_cluster(cluster_config) + assert resp == expected_data + + def test_delete_cluster(self, cluster_id, random_name, request_user, requests_mock): + expected_task = {"taskID": random_name} + requests_mock.delete(ANY, json={"code": 0, "data": {}, "task": expected_task}) + + client = BcsApiClient(ComponentAuth(request_user.token.access_token)) + resp = client.delete_cluster(cluster_id) + assert resp["task"] == expected_task + + def test_query_task(self, random_name, request_user, requests_mock): + expected_data = {"taskID": random_name} + requests_mock.get(ANY, json={"code": 0, "data": expected_data}) + + client = BcsApiClient(ComponentAuth(request_user.token.access_token)) + resp = client.query_task(random_name) + assert resp["data"] == expected_data diff --git a/bcs-app/backend/tests/container_service/clusters/test_cm.py b/bcs-app/backend/tests/container_service/clusters/test_cm.py new file mode 100644 index 000000000..37355a297 --- /dev/null +++ b/bcs-app/backend/tests/container_service/clusters/test_cm.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from unittest.mock import patch + +import pytest + +from backend.container_service.clusters import cm, constants +from backend.container_service.clusters.models import CommonStatus +from backend.tests.testing_utils.base import generate_random_string +from backend.tests.testing_utils.mocks import bcs_api, paas_cc + +fake_biz_id = 1 +fake_coes = "k8s" +fake_cluster_name = "test-cluster" +fake_cluster_data = { + "creator": "admin", + "environment": "test", + "name": fake_cluster_name, + "coes": fake_coes, + "area_id": 1, + "version": "1.12", +} + + +@pytest.fixture(autouse=True) +def pre_patch(): + with patch("backend.container_service.clusters.cm.paas_cc.PaaSCCClient", new=paas_cc.StubPaaSCCClient), patch( + "backend.container_service.clusters.cm.bcs_api.BcsApiClient", new=bcs_api.StubBcsApiClient + ): + yield + + +def test_create_cluster(project_id, cluster_id, request_user): + cluster_data = cm.create_cluster(request_user.token.access_token, project_id, fake_biz_id, fake_cluster_data) + assert cluster_data["type"] == fake_coes + + +def test_update_cluster(project_id, cluster_id, request_user): + cluster_data = cm.update_cluster( + request_user.token.access_token, project_id, cluster_id, {"name": fake_cluster_name} + ) + assert cluster_data["name"] == fake_cluster_name + + +def test_delete_cluster(project_id, cluster_id, request_user): + cluster_data = cm.delete_cluster(request_user.token.access_token, project_id, cluster_id) + assert cluster_data["status"] == CommonStatus.Removing diff --git a/bcs-app/backend/tests/container_service/projects/test_cm.py b/bcs-app/backend/tests/container_service/projects/test_cm.py new file mode 100644 index 000000000..398bd14e7 --- /dev/null +++ b/bcs-app/backend/tests/container_service/projects/test_cm.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from unittest.mock import patch + +from backend.container_service.projects.cm import update_or_create_project +from backend.tests.testing_utils.mocks import bcs_api, paas_cc + +fake_project_kind = 1 +fake_project_data = {"updator": "admin", "kind": fake_project_kind, "cc_app_id": 1} + + +@patch("backend.container_service.projects.cm.paas_cc.PaaSCCClient", new=paas_cc.StubPaaSCCClient) +@patch("backend.container_service.projects.cm.bcs_api.BcsApiClient", new=bcs_api.StubBcsApiClient) +def test_update_project(request_user, project_id): + """更新项目信息 + 项目已经存在于clustermanager中 + """ + project_data = update_or_create_project(request_user.token.access_token, project_id, fake_project_data) + assert project_data["kind"] == fake_project_kind + + +@patch("backend.container_service.projects.cm.paas_cc.PaaSCCClient", new=paas_cc.StubPaaSCCClient) +@patch( + "backend.container_service.projects.cm.bcs_api.BcsApiClient.create_project", + new=bcs_api.StubBcsApiClient().create_project, +) +@patch( + "backend.container_service.projects.cm.bcs_api.BcsApiClient.update_project", + new=bcs_api.StubBcsApiClient().update_project_not_exist, +) +def test_create_project(request_user, project_id): + project_data = update_or_create_project(request_user.token.access_token, project_id, fake_project_data) + assert project_data["kind"] == fake_project_kind diff --git a/bcs-app/backend/tests/testing_utils/mocks/bcs_api.py b/bcs-app/backend/tests/testing_utils/mocks/bcs_api.py index 4fe02257b..460be8bf2 100644 --- a/bcs-app/backend/tests/testing_utils/mocks/bcs_api.py +++ b/bcs-app/backend/tests/testing_utils/mocks/bcs_api.py @@ -5,14 +5,14 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://opensource.org/licenses/MIT - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ -from typing import Dict +from typing import Dict, List + +from backend.components import bcs_api from .utils import mockable_function @@ -30,3 +30,878 @@ def query_cluster_id(self, env_name: str, project_id: str, cluster_id: str) -> s @mockable_function def get_cluster_credentials(self, env_name: str, bcs_cluster_id: str) -> Dict: return {'server_address_path': '/foo', 'user_token': 'foo-token'} + + def create_project(self, *args, **kwargs) -> Dict: + return {"code": 0, "data": self.make_project_data()} + + def query_project(self, project_id) -> Dict: + return self.make_project_data() + + def update_project(self, *args, **kwargs) -> Dict: + return {"code": 0, "data": self.make_project_data()} + + def update_project_not_exist(self, *args, **kwargs) -> Dict: + return {"code": bcs_api.record_not_exist_code} + + def add_cluster(self, *args, **kwargs) -> Dict: + return {"code": 0, "data": self.make_add_cluster_resp()} + + def update_cluster(self, cluster_config: bcs_api.UpdatedClusterConfig) -> Dict: + return self.make_update_cluster_data() + + def delete_cluster( + self, cluster_id: str, is_force: bool = False, is_clean_resource=True, only_delete_info=False + ) -> Dict: + return {"code": 0, "data": self.make_delete_cluster_resp(cluster_id)} + + def add_nodes(self, cluster_id: str, node_config: bcs_api.NodeConfig) -> Dict: + return self.make_add_nodes_data() + + def delete_nodes(self, cluster_id: str, nodes: List[str], delete_mode: str, is_force: bool = False) -> Dict: + return self.make_delete_nodes_data() + + def query_task(self, task_id: str) -> Dict: + return self.make_query_task_data(task_id) + + @staticmethod + def make_project_data() -> Dict: + return { + "projectID": "string", + "name": "string", + "englishName": "string", + "creator": "string", + "updater": "string", + "projectType": 0, + "useBKRes": True, + "description": "string", + "isOffline": True, + "kind": "string", + "businessID": "string", + "deployType": 0, + "bgID": "string", + "bgName": "string", + "deptID": "string", + "deptName": "string", + "centerID": "string", + "centerName": "string", + "isSecret": True, + "credentials": { + "additionalProp1": {"key": "string", "secret": "string"}, + "additionalProp2": {"key": "string", "secret": "string"}, + "additionalProp3": {"key": "string", "secret": "string"}, + }, + "createTime": "string", + "updateTime": "string", + } + + @staticmethod + def make_add_cluster_resp() -> Dict: + return { + "code": 0, + "message": "string", + "result": True, + "data": { + "clusterID": "string", + "clusterName": "string", + "federationClusterID": "string", + "provider": "string", + "region": "string", + "vpcID": "string", + "projectID": "string", + "businessID": "string", + "environment": "string", + "engineType": "string", + "isExclusive": True, + "clusterType": "string", + "labels": {"additionalProp1": "string", "additionalProp2": "string", "additionalProp3": "string"}, + "creator": "string", + "createTime": "string", + "updateTime": "string", + "bcsAddons": { + "additionalProp1": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp2": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp3": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + }, + "extraAddons": { + "additionalProp1": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp2": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp3": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + }, + "systemID": "string", + "manageType": "string", + "master": { + "additionalProp1": { + "nodeID": "string", + "innerIP": "string", + "instanceType": "string", + "CPU": 0, + "mem": 0, + "GPU": 0, + "status": "string", + "zoneID": "string", + "nodeGroupID": "string", + "clusterID": "string", + }, + "additionalProp2": { + "nodeID": "string", + "innerIP": "string", + "instanceType": "string", + "CPU": 0, + "mem": 0, + "GPU": 0, + "status": "string", + "zoneID": "string", + "nodeGroupID": "string", + "clusterID": "string", + }, + "additionalProp3": { + "nodeID": "string", + "innerIP": "string", + "instanceType": "string", + "CPU": 0, + "mem": 0, + "GPU": 0, + "status": "string", + "zoneID": "string", + "nodeGroupID": "string", + "clusterID": "string", + }, + }, + "networkSettings": { + "clusterIPv4CIDR": "string", + "serviceIPv4CIDR": "string", + "maxNodePodNum": "string", + "maxServiceNum": "string", + }, + "clusterBasicSettings": { + "OS": "string", + "version": "string", + "clusterTags": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "clusterAdvanceSettings": { + "IPVS": True, + "containerRuntime": "string", + "runtimeVersion": "string", + "extraArgs": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "nodeSettings": {"dockerGraphPath": "string", "mountTarget": "string"}, + "status": "string", + "updator": "string", + }, + "task": { + "taskID": "string", + "taskType": "string", + "status": "string", + "message": "string", + "start": "string", + "end": "string", + "executionTime": 0, + "currentStep": "string", + "stepSequence": ["string"], + "steps": { + "additionalProp1": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp2": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp3": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + }, + "clusterID": "string", + "projectID": "string", + "creator": "string", + "lastUpdate": "string", + "updator": "string", + "forceTerminate": True, + }, + } + + @staticmethod + def make_update_cluster_data() -> Dict: + { + "clusterID": "string", + "clusterName": "string", + "federationClusterID": "string", + "provider": "string", + "region": "string", + "vpcID": "string", + "projectID": "string", + "businessID": "string", + "environment": "string", + "engineType": "string", + "isExclusive": True, + "clusterType": "string", + "labels": {"additionalProp1": "string", "additionalProp2": "string", "additionalProp3": "string"}, + "creator": "string", + "createTime": "string", + "updateTime": "string", + "bcsAddons": { + "additionalProp1": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp2": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp3": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + }, + "extraAddons": { + "additionalProp1": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp2": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp3": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + }, + "systemID": "string", + "manageType": "string", + "master": { + "additionalProp1": { + "nodeID": "string", + "innerIP": "string", + "instanceType": "string", + "CPU": 0, + "mem": 0, + "GPU": 0, + "status": "string", + "zoneID": "string", + "nodeGroupID": "string", + "clusterID": "string", + }, + "additionalProp2": { + "nodeID": "string", + "innerIP": "string", + "instanceType": "string", + "CPU": 0, + "mem": 0, + "GPU": 0, + "status": "string", + "zoneID": "string", + "nodeGroupID": "string", + "clusterID": "string", + }, + "additionalProp3": { + "nodeID": "string", + "innerIP": "string", + "instanceType": "string", + "CPU": 0, + "mem": 0, + "GPU": 0, + "status": "string", + "zoneID": "string", + "nodeGroupID": "string", + "clusterID": "string", + }, + }, + "networkSettings": { + "clusterIPv4CIDR": "string", + "serviceIPv4CIDR": "string", + "maxNodePodNum": "string", + "maxServiceNum": "string", + }, + "clusterBasicSettings": { + "OS": "string", + "version": "string", + "clusterTags": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "clusterAdvanceSettings": { + "IPVS": True, + "containerRuntime": "string", + "runtimeVersion": "string", + "extraArgs": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "nodeSettings": {"dockerGraphPath": "string", "mountTarget": "string"}, + "status": "string", + "updator": "string", + } + + @staticmethod + def make_delete_cluster_resp(cluster_id: str) -> Dict: + return { + "code": 0, + "message": "string", + "result": True, + "data": { + "clusterID": cluster_id, + "clusterName": "string", + "federationClusterID": "string", + "provider": "string", + "region": "string", + "vpcID": "string", + "projectID": "string", + "businessID": "string", + "environment": "string", + "engineType": "string", + "isExclusive": True, + "clusterType": "string", + "labels": {"additionalProp1": "string", "additionalProp2": "string", "additionalProp3": "string"}, + "creator": "string", + "createTime": "string", + "updateTime": "string", + "bcsAddons": { + "additionalProp1": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp2": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp3": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + }, + "extraAddons": { + "additionalProp1": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp2": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp3": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + }, + "systemID": "string", + "manageType": "string", + "master": { + "additionalProp1": { + "nodeID": "string", + "innerIP": "string", + "instanceType": "string", + "CPU": 0, + "mem": 0, + "GPU": 0, + "status": "string", + "zoneID": "string", + "nodeGroupID": "string", + "clusterID": "string", + }, + "additionalProp2": { + "nodeID": "string", + "innerIP": "string", + "instanceType": "string", + "CPU": 0, + "mem": 0, + "GPU": 0, + "status": "string", + "zoneID": "string", + "nodeGroupID": "string", + "clusterID": "string", + }, + "additionalProp3": { + "nodeID": "string", + "innerIP": "string", + "instanceType": "string", + "CPU": 0, + "mem": 0, + "GPU": 0, + "status": "string", + "zoneID": "string", + "nodeGroupID": "string", + "clusterID": "string", + }, + }, + "networkSettings": { + "clusterIPv4CIDR": "string", + "serviceIPv4CIDR": "string", + "maxNodePodNum": "string", + "maxServiceNum": "string", + }, + "clusterBasicSettings": { + "OS": "string", + "version": "string", + "clusterTags": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "clusterAdvanceSettings": { + "IPVS": True, + "containerRuntime": "string", + "runtimeVersion": "string", + "extraArgs": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "nodeSettings": {"dockerGraphPath": "string", "mountTarget": "string"}, + "status": "string", + "updator": "string", + }, + "tasks": [ + { + "taskID": "string", + "taskType": "string", + "status": "string", + "message": "string", + "start": "string", + "end": "string", + "executionTime": 0, + "currentStep": "string", + "stepSequence": ["string"], + "steps": { + "additionalProp1": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp2": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp3": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + }, + "clusterID": "string", + "projectID": "string", + "creator": "string", + "lastUpdate": "string", + "updator": "string", + "forceTerminate": True, + } + ], + } + + @staticmethod + def make_add_nodes_data() -> Dict: + return { + "taskID": "string", + "taskType": "string", + "status": "string", + "message": "string", + "start": "string", + "end": "string", + "executionTime": 0, + "currentStep": "string", + "stepSequence": ["string"], + "steps": { + "additionalProp1": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp2": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp3": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + }, + "clusterID": "string", + "projectID": "string", + "creator": "string", + "lastUpdate": "string", + "updator": "string", + "forceTerminate": True, + } + + @staticmethod + def make_delete_nodes_data() -> Dict: + return { + "taskID": "string", + "taskType": "string", + "status": "string", + "message": "string", + "start": "string", + "end": "string", + "executionTime": 0, + "currentStep": "string", + "stepSequence": ["string"], + "steps": { + "additionalProp1": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp2": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp3": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + }, + "clusterID": "string", + "projectID": "string", + "creator": "string", + "lastUpdate": "string", + "updator": "string", + "forceTerminate": True, + } + + @staticmethod + def make_query_task_data(task_id: str) -> Dict: + return { + "taskID": task_id, + "taskType": "string", + "status": "string", + "message": "string", + "start": "string", + "end": "string", + "executionTime": 0, + "currentStep": "string", + "stepSequence": ["string"], + "steps": { + "additionalProp1": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp2": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp3": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + }, + "clusterID": "string", + "projectID": "string", + "creator": "string", + "lastUpdate": "string", + "updator": "string", + "forceTerminate": True, + } diff --git a/bcs-app/backend/tests/testing_utils/mocks/bk_cc.py b/bcs-app/backend/tests/testing_utils/mocks/bk_cc.py index 3da1b4308..7c3470590 100644 --- a/bcs-app/backend/tests/testing_utils/mocks/bk_cc.py +++ b/bcs-app/backend/tests/testing_utils/mocks/bk_cc.py @@ -5,35 +5,165 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://opensource.org/licenses/MIT - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ -from typing import Dict, List, Optional +import uuid +from typing import Dict, List + +from backend.container_service.projects.base.constants import ProjectKind -from backend.components.cc import PageData +from .utils import mockable_function -class FakeBkCCClient: +class StubPaaSCCClient: + """使用假数据的 PaaSCCClient 对象""" + def __init__(self, *args, **kwargs): pass - def search_biz(self, page: PageData, fields: Optional[List] = None, condition: Optional[Dict] = None) -> Dict: + @mockable_function + def get_cluster(self, project_id: str, cluster_id: str) -> Dict: + return self.wrap_resp(self.make_cluster_data(project_id, cluster_id)) + + @mockable_function + def get_project(self, project_id: str) -> Dict: + return self.make_project_data(project_id) + + @mockable_function + def get_mesos_project(self, project_id: str) -> Dict: + """返回mesos项目信息""" + data = self.make_project_data(project_id) + data["kind"] = ProjectKind.MESOS.value + return data + + @mockable_function + def get_cluster_namespace_list(self, project_id: str, cluster_id: str) -> Dict: + return self.make_cluster_namespace_data(project_id, cluster_id) + + @mockable_function + def update_project(self, project_id: str, *args, **kwargs) -> Dict: + return self.make_project_data(project_id) + + @mockable_function + def create_cluster(self, project_id: str, cluster_id: str, *args, **kwargs) -> Dict: + return self.make_cluster_data(project_id, cluster_id) + + @mockable_function + def update_cluster(self, project_id: str, cluster_id: str, *args, **kwargs) -> Dict: + return self.make_cluster_data(project_id, cluster_id) + + @mockable_function + def add_nodes(self, project_id: str, cluster_id: str) -> List: + return self.make_nodes_data(project_id, cluster_id) + + @mockable_function + def update_node_list(self, project_id: str, cluster_id: str) -> List: + return self.make_nodes_data(project_id, cluster_id) + + @staticmethod + def wrap_resp(data): + return { + 'code': 0, + 'data': data, + 'message': '', + 'request_id': uuid.uuid4().hex, + 'result': True, + } + + @staticmethod + def make_cluster_data(project_id: str, cluster_id: str): + _stub_time = '2021-01-01T00:00:00+08:00' + return { + 'area_id': 1, + 'artifactory': '', + 'capacity_updated_at': _stub_time, + 'cluster_id': cluster_id, + 'cluster_num': 1, + 'config_svr_count': 0, + 'created_at': _stub_time, + 'creator': 'unknown', + 'description': 'cluster description', + 'disabled': False, + 'environment': 'stag', + 'extra_cluster_id': '', + 'ip_resource_total': 0, + 'ip_resource_used': 0, + 'master_count': 0, + 'name': 'test-cluster', + 'need_nat': True, + 'node_count': 1, + 'project_id': project_id, + 'remain_cpu': 10, + 'remain_disk': 0, + 'remain_mem': 10, + 'status': 'normal', + 'total_cpu': 12, + 'total_disk': 0, + 'total_mem': 64, + 'type': 'k8s', + 'updated_at': _stub_time, + } + + @staticmethod + def make_project_data(project_id: str): + _stub_time = '2021-01-01T00:00:00+08:00' + return { + "approval_status": 2, + "approval_time": "2020-01-01T00:00:00+08:00", + "approver": "", + "bg_id": -1, + "bg_name": "", + "cc_app_id": 100, + "center_id": 100, + "center_name": "", + "created_at": "2020-01-01 00:00:00", + "creator": "unknown", + "data_id": 0, + "deploy_type": "null", + "dept_id": -1, + "dept_name": "", + "description": "", + "english_name": "unittest-cluster", + "extra": {}, + "is_offlined": False, + "is_secrecy": False, + "kind": 1, + "logo_addr": "", + "project_id": project_id, + "project_name": "unittest-cluster", + "project_type": 1, + "remark": "", + "updated_at": "2020-01-01 00:00:00", + "use_bk": False, + "cc_app_name": "demo-app", + "can_edit": False, + } + + @staticmethod + def make_cluster_namespace_data(project_id: str, cluster_id: str) -> Dict: + _stub_time = '2021-06-30T11:13:00+08:00' return { "count": 1, - "info": [ + "results": [ { - "bs2_name_id": 1, - "bk_oper_plan": "admin", - "bk_biz_developer": "admin", - "bk_biz_maintainer": "admin", - "bk_dept_name_id": 1, - "bk_biz_name": "demo", - "bk_product_name": "demo", - "default": 0, + "cluster_id": cluster_id, + "created_at": _stub_time, + "creator": "admin", + "description": "", + "env_type": "dev", + "has_image_secret": True, + "id": 1, + "name": "default", + "project_id": project_id, + "status": "", + "updated_at": _stub_time, } ], } + + @staticmethod + def make_nodes_data(project_id: str, cluster_id: str) -> List: + return [{"id": 1, "inner_ip": "127.0.0.1", "project_id": project_id, "cluster_id": cluster_id}] From 3319921eabedd813c7044e1739ba991450dd20f6 Mon Sep 17 00:00:00 2001 From: bellkeyang Date: Mon, 6 Sep 2021 14:54:39 +0800 Subject: [PATCH 02/10] feat: write date to cluster manager --- bcs-app/backend/components/bcs_api.py | 2 ++ bcs-app/backend/components/paas_cc.py | 2 ++ bcs-app/backend/container_service/clusters/cm.py | 2 ++ bcs-app/backend/container_service/clusters/constants.py | 2 ++ bcs-app/backend/container_service/projects/auditor.py | 2 ++ bcs-app/backend/container_service/projects/cm.py | 2 ++ bcs-app/backend/container_service/projects/views.py | 7 +++---- bcs-app/backend/tests/components/test_bcs_api.py | 2 ++ .../backend/tests/container_service/clusters/test_cm.py | 2 ++ .../backend/tests/container_service/projects/test_cm.py | 2 ++ bcs-app/backend/tests/testing_utils/mocks/bcs_api.py | 2 ++ bcs-app/backend/tests/testing_utils/mocks/bk_cc.py | 2 ++ 12 files changed, 25 insertions(+), 4 deletions(-) diff --git a/bcs-app/backend/components/bcs_api.py b/bcs-app/backend/components/bcs_api.py index 4d42ef2aa..3aefc6ba6 100644 --- a/bcs-app/backend/components/bcs_api.py +++ b/bcs-app/backend/components/bcs_api.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/components/paas_cc.py b/bcs-app/backend/components/paas_cc.py index 0412796c4..cdfcb4bb4 100644 --- a/bcs-app/backend/components/paas_cc.py +++ b/bcs-app/backend/components/paas_cc.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/container_service/clusters/cm.py b/bcs-app/backend/container_service/clusters/cm.py index fffcdb837..ad5ce5fd6 100644 --- a/bcs-app/backend/container_service/clusters/cm.py +++ b/bcs-app/backend/container_service/clusters/cm.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/container_service/clusters/constants.py b/bcs-app/backend/container_service/clusters/constants.py index 26bb012c9..93c4a1eec 100644 --- a/bcs-app/backend/container_service/clusters/constants.py +++ b/bcs-app/backend/container_service/clusters/constants.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/container_service/projects/auditor.py b/bcs-app/backend/container_service/projects/auditor.py index 92a3c0594..5511a2512 100644 --- a/bcs-app/backend/container_service/projects/auditor.py +++ b/bcs-app/backend/container_service/projects/auditor.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/container_service/projects/cm.py b/bcs-app/backend/container_service/projects/cm.py index 1b533b610..7b49a1448 100644 --- a/bcs-app/backend/container_service/projects/cm.py +++ b/bcs-app/backend/container_service/projects/cm.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/container_service/projects/views.py b/bcs-app/backend/container_service/projects/views.py index fb3ce82ac..5a87e410e 100644 --- a/bcs-app/backend/container_service/projects/views.py +++ b/bcs-app/backend/container_service/projects/views.py @@ -5,30 +5,29 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ import json import logging -from typing import Dict from django.conf import settings from django.utils.translation import ugettext_lazy as _ from rest_framework import permissions, viewsets from rest_framework.renderers import BrowsableAPIRenderer -from rest_framework.request import Request from rest_framework.response import Response from rest_framework.views import APIView -from backend.bcs_web.audit_log import client from backend.bcs_web.audit_log.audit.decorators import log_audit_on_view from backend.bcs_web.audit_log.constants import ActivityType from backend.bcs_web.constants import bcs_project_cache_key from backend.bcs_web.iam.permissions import ProjectPermission from backend.bcs_web.viewsets import SystemViewSet -from backend.components import bcs_api, paas_cc +from backend.components import paas_cc from backend.container_service.projects import base as Project from backend.container_service.projects.utils import ( get_app_by_user_role, diff --git a/bcs-app/backend/tests/components/test_bcs_api.py b/bcs-app/backend/tests/components/test_bcs_api.py index 72a2bdb3b..a1c15190c 100644 --- a/bcs-app/backend/tests/components/test_bcs_api.py +++ b/bcs-app/backend/tests/components/test_bcs_api.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/tests/container_service/clusters/test_cm.py b/bcs-app/backend/tests/container_service/clusters/test_cm.py index 37355a297..f44f6767d 100644 --- a/bcs-app/backend/tests/container_service/clusters/test_cm.py +++ b/bcs-app/backend/tests/container_service/clusters/test_cm.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/tests/container_service/projects/test_cm.py b/bcs-app/backend/tests/container_service/projects/test_cm.py index 398bd14e7..6a6744de3 100644 --- a/bcs-app/backend/tests/container_service/projects/test_cm.py +++ b/bcs-app/backend/tests/container_service/projects/test_cm.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/tests/testing_utils/mocks/bcs_api.py b/bcs-app/backend/tests/testing_utils/mocks/bcs_api.py index 460be8bf2..ebfcca56e 100644 --- a/bcs-app/backend/tests/testing_utils/mocks/bcs_api.py +++ b/bcs-app/backend/tests/testing_utils/mocks/bcs_api.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/bcs-app/backend/tests/testing_utils/mocks/bk_cc.py b/bcs-app/backend/tests/testing_utils/mocks/bk_cc.py index 7c3470590..94f3235c1 100644 --- a/bcs-app/backend/tests/testing_utils/mocks/bk_cc.py +++ b/bcs-app/backend/tests/testing_utils/mocks/bk_cc.py @@ -5,7 +5,9 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://opensource.org/licenses/MIT + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. From 8da2ba82b328bafa1bc6cbb2231db4de8751f372 Mon Sep 17 00:00:00 2001 From: bellkeyang Date: Tue, 14 Sep 2021 19:41:07 +0800 Subject: [PATCH 03/10] feat: write data to cluster manager --- .../{bcs_api.py => bcs_api/__init__.py} | 7 +- bcs-app/backend/components/bcs_api/cluster.py | 206 +++++ .../backend/components/bcs_api/constants.py | 53 ++ bcs-app/backend/components/bcs_api/project.py | 131 +++ bcs-app/backend/components/paas_cc.py | 39 +- .../clusters/cluster_manager.py | 123 +++ .../container_service/clusters/constants.py | 18 + .../container_service/projects/auditor.py | 23 + .../container_service/projects/views.py | 49 +- bcs-app/backend/settings/ce/base.py | 2 + .../tests/components/bcs_api/test_cluster.py | 87 ++ .../tests/components/bcs_api/test_project.py | 59 ++ .../backend/tests/components/test_bcs_api.py | 3 +- .../clusters/test_cluster_manager.py | 64 ++ .../projects/test_project_manager.py | 49 + .../mocks/{bcs_api.py => bcs_api/__init__.py} | 2 +- .../testing_utils/mocks/bcs_api/cluster.py | 856 ++++++++++++++++++ .../testing_utils/mocks/bcs_api/project.py | 67 ++ .../tests/testing_utils/mocks/paas_cc.py | 41 +- 19 files changed, 1813 insertions(+), 66 deletions(-) rename bcs-app/backend/components/{bcs_api.py => bcs_api/__init__.py} (97%) create mode 100644 bcs-app/backend/components/bcs_api/cluster.py create mode 100644 bcs-app/backend/components/bcs_api/constants.py create mode 100644 bcs-app/backend/components/bcs_api/project.py create mode 100644 bcs-app/backend/container_service/clusters/cluster_manager.py create mode 100644 bcs-app/backend/container_service/projects/auditor.py create mode 100644 bcs-app/backend/tests/components/bcs_api/test_cluster.py create mode 100644 bcs-app/backend/tests/components/bcs_api/test_project.py create mode 100644 bcs-app/backend/tests/container_service/clusters/test_cluster_manager.py create mode 100644 bcs-app/backend/tests/container_service/projects/test_project_manager.py rename bcs-app/backend/tests/testing_utils/mocks/{bcs_api.py => bcs_api/__init__.py} (97%) create mode 100644 bcs-app/backend/tests/testing_utils/mocks/bcs_api/cluster.py create mode 100644 bcs-app/backend/tests/testing_utils/mocks/bcs_api/project.py diff --git a/bcs-app/backend/components/bcs_api.py b/bcs-app/backend/components/bcs_api/__init__.py similarity index 97% rename from bcs-app/backend/components/bcs_api.py rename to bcs-app/backend/components/bcs_api/__init__.py index 4c30ed28a..b905fa28d 100644 --- a/bcs-app/backend/components/bcs_api.py +++ b/bcs-app/backend/components/bcs_api/__init__.py @@ -18,7 +18,7 @@ from requests import PreparedRequest from requests.auth import AuthBase -from .base import BaseHttpClient, BkApiClient, ComponentAuth, update_url_parameters +from ..base import BaseHttpClient, BkApiClient, ComponentAuth, update_url_parameters class BcsApiConfig: @@ -51,12 +51,9 @@ def __call__(self, r: PreparedRequest): class BcsApiClient(BkApiClient): """访问 BCS API 服务的 Client 对象 - :param auth: 包含校验信息的对象 - API 方法常用请求参数说明 === - :param env_name: 集群环境,比如 stag/prod :param project_id: 项目 ID :param cluster_id: 集群 ID @@ -69,7 +66,6 @@ def __init__(self, auth: ComponentAuth): def query_cluster_id(self, env_name: str, project_id: str, cluster_id: str) -> str: """查询集群在 BCS-Api 中的 ID - :returns: 集群 ID 字符串 """ url = self._config.query_cluster_id_url.format(env_name=env_name) @@ -81,7 +77,6 @@ def query_cluster_id(self, env_name: str, project_id: str, cluster_id: str) -> s def get_cluster_credentials(self, env_name: str, bcs_cluster_id: str) -> Dict: """ 获取访问集群 apiserver 所需的鉴权信息,比如证书、user_token、server_address_path 等 - :returns: 包含集群鉴权信息的字典 """ url = self._config.get_cluster_credentials_url.format(env_name=env_name, bcs_cluster_id=bcs_cluster_id) diff --git a/bcs-app/backend/components/bcs_api/cluster.py b/bcs-app/backend/components/bcs_api/cluster.py new file mode 100644 index 000000000..66a3b4317 --- /dev/null +++ b/bcs-app/backend/components/bcs_api/cluster.py @@ -0,0 +1,206 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from typing import Dict, List + +from attr import asdict, dataclass +from django.conf import settings + +from ..base import BaseHttpClient, BkApiClient, ComponentAuth, response_handler +from . import BcsApiAuth +from .constants import BCS_API_VERSION, ClusterManageType, ManageType + + +class BcsApiGatewayConfig: + def __init__(self, host: str, env: str): + self.host = host + + _cluster_api_url_prefix = f"{host}/{env}/{BCS_API_VERSION}/clustermanager/v1" + # cluster manager中集群相关接口url + self.add_cluster_url = f"{_cluster_api_url_prefix}/cluster/{{cluster_id}}" + self.delete_cluster_url = f"{_cluster_api_url_prefix}/cluster/{{cluster_id}}" + self.update_cluster_url = f"{_cluster_api_url_prefix}/cluster/{{cluster_id}}" + self.add_nodes_url = f"{_cluster_api_url_prefix}/cluster/{{cluster_id}}/node" + self.delete_nodes_url = f"{_cluster_api_url_prefix}/cluster/{{cluster_id}}/node" + self.query_task_url = f"{_cluster_api_url_prefix}/task/{{task_id}}" + + +@dataclass +class CloudClusterConfig: + """集群初始化需要的基本配置 + region: 区域 + manageType: 集群管理类型,公有云时生效,MANAGED_CLUSTER(云上托管集群),INDEPENDENT_CLUSTER(独立集群,自行维护) + master: 集群master + vpcID: vpc ID + cloudID: 所属云ID + nodes: 集群的节点 + networkSettings: 网络配置 + clusterBasicSettings: 集群的基本配置 + clusterAdvanceSettings: 集群的高级配置 + nodeSettings: 节点配置信息 + systemReinstall: 是否重装master节点的系统,机器被托管情况下有效 + initLoginPassword: 重装master节点的系统时,初始化password,机器被托管情况下有效 + status: 状态 + """ + + region: str + master: List + manageType: str = ManageType.MANAGED_CLUSTER + vpcID: str = "" + cloudID: str = "" + nodes: List = [] + networkSettings: Dict = {} + clusterBasicSettings: Dict = {} + clusterAdvanceSettings: Dict = {} + nodeSettings: Dict = {} + systemReinstall: bool = False + initLoginPassword: str = "" + status: str = "" + + +@dataclass +class BcsBasicConfig: + """集群初始化时,BCS需要的基本配置""" + + projectID: str + businessID: str + clusterID: str + clusterName: str + provider: str + environment: str # 集群环境,如prod、debug、test + engineType: str # 引擎类型,如k8s + clusterType: str = ClusterManageType.SINGLE + isExclusive: bool = False # 是否为独占集群 + federationClusterID: str = "" # 联邦集群ID + labels: Dict = {} + onlyCreateInfo: bool = False + bcsAddons: Dict = {} + extraAddons: Dict = {} + + +@dataclass +class ClusterConfig: + """集群配置""" + + creator: str + bcs_basic_config: BcsBasicConfig + cloud_cluster_config: CloudClusterConfig + + +@dataclass +class UpdatedClusterConfig: + """更新的集群配置选项""" + + projectID: str + clusterID: str + updater: str + status: str + clusterName: str = "" + labels: Dict = {} + + +@dataclass +class NodeConfig: + nodes: List[str] + nodeGroupID: str = "" + onlyCreateInfo: bool = False + initLoginPassword: str = "" + + +class BcsClusterApiClient(BkApiClient): + """访问 BCS 集群 API 服务的 Client 对象 + :param auth: 包含校验信息的对象 + """ + + def __init__(self, auth: ComponentAuth): + self._config = BcsApiGatewayConfig(host=settings.BCS_API_GW_DOMAIN, env=settings.BCS_API_GW_ENV) + self._client = BaseHttpClient(BcsApiAuth(auth.access_token)) + + def add_cluster(self, cluster_config: ClusterConfig) -> Dict: + """添加集群 + :param creator: 创建者 + :param cluster_config: 集群初始化的配置,包含bcs平台需要的信息和集群初始化需要的信息 + :return: 返回初始化的集群的信息,任务信息,格式: {"data": {}, "task": {}} + """ + # 组装参数 + cluster_config_data = asdict(cluster_config.cloud_cluster_config) + cluster_config_data.update(asdict(cluster_config.bcs_basic_config), creator=cluster_config.creator) + # 下发初始化配置 + url = self._config.add_cluster_url.format(cluster_id=cluster_config_data["clusterID"]) + return self._client.request_json("POST", url, json=cluster_config_data) + + @response_handler() + def update_cluster(self, cluster_config: UpdatedClusterConfig) -> Dict: + """更新集群 + :param cluster_config: 更新集群的配置 + :return: 返回集群的配置 + """ + # 组装参数,并去掉为None的属性 + cluster_config_data = {k: v for k, v in asdict(cluster_config).items() if v is not None} + # 下发更新的配置 + url = self._config.update_cluster_url.format(cluster_id=cluster_config_data["clusterID"]) + return self._client.request_json("PUT", url, json=cluster_config_data) + + def delete_cluster( + self, + cluster_id: str, + is_force: bool = False, + is_clean_resource: bool = True, + only_delete_info: bool = False, + ) -> Dict: + """删除集群 + :param cluster_id: 集群ID + :param is_force: 是否强制删除,默认为False + :param is_clean_resource: 强制删除有效时,是否清理机器上部署的资源 + :param only_delete_info: 是否仅删除记录的信息,默认为False + :return: 返回删除的集群信息,任务信息 + """ + url = self._config.delete_cluster_url.format(cluster_id=cluster_id) + params = {"isForced": is_force, "resourceClean": is_clean_resource, "onlyDeleteInfo": only_delete_info} + return self._client.request_json("DELETE", url, params=params) + + @response_handler() + def add_nodes(self, cluster_id: str, node_config: NodeConfig) -> Dict: + """添加节点 + :param cluster_id: 集群ID + :param node_config: 节点配置 + :return: 返回节点的数据,包含任务ID + """ + url = self._config.add_nodes_url.format(cluster_id=cluster_id) + data = asdict(node_config) + data["clusterID"] = cluster_id + return self._client.request_json("POST", url, json=data) + + @response_handler() + def delete_nodes( + self, cluster_id: str, nodes: List[str], delete_mode: str = "RETAIN", is_force: bool = False + ) -> Dict: + """删除节点 + :param cluster_id: 集群ID + :param nodes: 节点列表 + :param delete_mode: 删除模式,RETAIN(移除集群,但是保留主机),TERMINATE(只支持按量计费的机器) + :param is_force: 是否强制删除 + :return: 返回删除的节点信息,包含任务ID + """ + url = self._config.delete_nodes_url.format(cluster_id=cluster_id) + data = {"clusterID": cluster_id, "nodes": nodes, "deleteMode": delete_mode, "isForce": is_force} + return self._client.request_json("DELETE", url, json=data) + + def query_task(self, task_id: str) -> Dict: + """查询任务状态 + :param task_id: 任务ID + :return: 返回任务的执行详情 + """ + url = self._config.query_task_url.format(task_id=task_id) + return self._client.request_json("GET", url) diff --git a/bcs-app/backend/components/bcs_api/constants.py b/bcs-app/backend/components/bcs_api/constants.py new file mode 100644 index 000000000..9bd46c9ba --- /dev/null +++ b/bcs-app/backend/components/bcs_api/constants.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from django.utils.translation import ugettext as _ + +from backend.packages.blue_krill.data_types.enum import EnumField, StructuredEnum + +# bcs api的版本 +BCS_API_VERSION = "v4" + +# 资源记录不存在的code +RECORD_NOT_EXIST_CODE = 1405420 +# 资源记录存在的code +RECORD_EXIST_CODE = 1405405 + + +class DeployType(str, StructuredEnum): + """部署类型, 使用物理机或者容器部署""" + + Physical = EnumField(1, label=_("物理机部署")) + Container = EnumField(2, label=_("容器部署")) + + +class ProjectType(str, StructuredEnum): + """项目类型,项目属于平台或者业务""" + + Platform = EnumField(1, label="platform") + Business = EnumField(2, label="business") + + +class ManageType(str, StructuredEnum): + """集群管理类型""" + + MANAGED_CLUSTER = EnumField("MANAGED_CLUSTER", label=_("云上托管集群")) + INDEPENDENT_CLUSTER = EnumField("INDEPENDENT_CLUSTER", label=_("独立集群,自行维护")) + + +class ClusterManageType(str, StructuredEnum): + """集群管理类型""" + + SINGLE = EnumField("single", label=_("独立集群")) + FEDERATION = EnumField("federation", label=_("联邦集群")) diff --git a/bcs-app/backend/components/bcs_api/project.py b/bcs-app/backend/components/bcs_api/project.py new file mode 100644 index 000000000..f2bd791e2 --- /dev/null +++ b/bcs-app/backend/components/bcs_api/project.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from typing import Dict + +from attr import asdict, dataclass +from django.conf import settings + +from ..base import BaseHttpClient, BkApiClient, ComponentAuth, response_handler +from . import BcsApiAuth +from .constants import BCS_API_VERSION, DeployType, ProjectType + + +class BcsApiGatewayConfig: + def __init__(self, host: str, env: str): + self.host = host + + # cluster manager相关接口url + _project_api_url_prefix = f"{host}/{env}/{BCS_API_VERSION}/clustermanager/v1/project" + self.query_project_url = f"{_project_api_url_prefix}/{{project_id}}" + self.create_project_url = f"{_project_api_url_prefix}/{{project_id}}" + self.update_project_url = f"{_project_api_url_prefix}/{{project_id}}" + + +@dataclass +class ProjectReservedConfig: + bgID: str = "" + bgName: str = "" + deptID: str = "" + deptName: str = "" + centerID: str = "" + centerName: str = "" + isSecret: bool = False # 标识项目是否有私密项目,默认为False + deployType: int = DeployType.Physical # 业务部署类型,1:物理机部署,2:容器部署 + isOffline: bool = False # 项目是否已经离线,默认False + useBKRes: bool = False # 是否使用蓝鲸提供的资源池,主要用于资源计费,默认False + projectType: int = ProjectType.Platform # 1:platform,2:business + + +@dataclass +class ProjectBasicConfig: + """项目的基本配置 + projectID: 项目ID,长度为32位字符串 + name: 项目名称 + englishName: 项目英文缩写,长度不能超过32字符 + kind: 项目中集群类型,支持k8s + businessID: 项目绑定的BK CC的业务ID + credentials: 记录的账户信息 + description: 项目的描述信息,默认为空 + """ + + projectID: str + name: str + englishName: str + kind: str + businessID: str + description: str + credentials: Dict = {} + + +@dataclass +class ProjectConfig: + """项目配置信息""" + + creator: str + basic_config: ProjectBasicConfig + reserved_config: ProjectReservedConfig + + +@dataclass +class UpdatedProjectConfig: + """更新项目配置信息""" + + projectID: str + updater: str + name: str + kind: str + businessID: str + + +class BcsProjectApiClient(BkApiClient): + """访问 BCS 项目 API 服务的 Client 对象 + :param auth: 包含校验信息的对象 + """ + + def __init__(self, auth: ComponentAuth): + self._config = BcsApiGatewayConfig(host=settings.BCS_API_GW_DOMAIN, env=settings.BCS_API_GW_ENV) + self._client = BaseHttpClient(BcsApiAuth(auth.access_token)) + + @response_handler() + def query_project(self, project_id: str) -> Dict: + """查询项目信息 + :param project_id: 项目ID + :return: 项目信息 + """ + url = self._config.query_project_url.format(project_id=project_id) + return self._client.request_json("GET", url) + + def create_project(self, project_config: ProjectConfig) -> Dict: + """创建项目 + :param project_config: 项目信息,包含项目的基本信息和项目的保留字段信息 + :return: 项目信息 + """ + # 组装参数 + project_config_data = asdict(project_config.basic_config) + project_config_data.update(asdict(project_config.reserved_config), creator=project_config.creator) + # 下发配置 + url = self._config.create_project_url.format(project_id=project_config_data["projectID"]) + return self._client.request_json("POST", url, json=project_config_data) + + def update_project(self, project_config: UpdatedProjectConfig) -> Dict: + """更新项目 + :param project_config: 更新的项目信息 + :return: 更新的项目信息 + """ + # 组装参数 + project_config_data = asdict(project_config) + # 下发配置 + url = self._config.create_project_url.format(project_id=project_config_data["projectID"]) + return self._client.request_json("PUT", url, json=project_config_data) diff --git a/bcs-app/backend/components/paas_cc.py b/bcs-app/backend/components/paas_cc.py index f8afcc170..cdfcb4bb4 100644 --- a/bcs-app/backend/components/paas_cc.py +++ b/bcs-app/backend/components/paas_cc.py @@ -521,6 +521,9 @@ def __init__(self, host: str): self.update_node_list_url = f"{host}/projects/{{project_id}}/clusters/{{cluster_id}}/nodes/" self.get_cluster_namespace_list_url = f"{host}/projects/{{project_id}}/clusters/{{cluster_id}}/namespaces/" self.get_node_list_url = f"{host}/projects/{{project_id}}/nodes/" + self.update_project_url = f"{host}/projects/{{project_id}}/" + self.add_cluster_url = f"{host}/projects/{{project_id}}/clusters/" + self.add_nodes_url = f"{host}/projects/{{project_id}}/clusters/{{cluster_id}}/nodes/" @dataclass @@ -531,7 +534,6 @@ class UpdateNodesData: class PaaSCCClient(BkApiClient): """访问 PaaSCC 服务的 Client 对象 - :param auth: 包含校验信息的对象 """ @@ -553,7 +555,6 @@ def get_project(self, project_id: str) -> Dict: @response_handler() def update_cluster(self, project_id: str, cluster_id: str, data: Dict) -> Dict: """更新集群信息 - :param project_id: 项目32为长度 ID :param cluster_id: 集群ID :param data: 更新的集群属性,包含status,名称、描述等 @@ -564,7 +565,6 @@ def update_cluster(self, project_id: str, cluster_id: str, data: Dict) -> Dict: @response_handler() def delete_cluster(self, project_id: str, cluster_id: str): """删除集群 - :param project_id: 项目32为长度 ID :param cluster_id: 集群ID """ @@ -574,7 +574,6 @@ def delete_cluster(self, project_id: str, cluster_id: str): @response_handler() def update_node_list(self, project_id: str, cluster_id: str, nodes: List[UpdateNodesData]) -> List: """更新节点信息 - :param project_id: 项目32为长度 ID :param cluster_id: 集群ID :param nodes: 更新的节点属性,包含IP和状态 @@ -594,7 +593,6 @@ def get_cluster_namespace_list( desire_all_data: Union[bool, int] = None, ) -> Dict[str, Union[int, List[Dict]]]: """获取集群下命名空间列表 - :param project_id: 项目ID :param cluster_id: 集群ID :param limit: 每页的数量 @@ -633,6 +631,37 @@ def get_node_list(self, project_id: str, cluster_id: str, params: Dict = None) - req_params.setdefault("desire_all_data", 1) return self._client.request_json("GET", url, params=req_params) + @response_handler() + def update_project(self, project_id: str, data: Dict) -> Dict: + """更新项目信息 + :param project_id: 项目ID + :param data: 要更新的项目信息 + :returns: 返回更新后的项目信息 + """ + url = self._config.update_project_url.format(project_id=project_id) + return self._client.request_json("PUT", url, json=data) + + @response_handler() + def create_cluster(self, project_id: str, data: Dict) -> Dict: + """创建集群信息 + :param project_id: 项目ID + :param data: 集群信息 + :returns: 返回创建的集群信息 + """ + url = self._config.add_cluster_url.format(project_id=project_id) + return self._client.request_json("POST", url, json=data) + + @response_handler() + def add_nodes(self, project_id: str, cluster_id: str, data: Dict) -> List: + """添加节点信息 + :param project_id: 项目ID + :param cluster_id: 集群ID + :param data: 添加的节点信息 + :returns: 返回节点数据 + """ + url = self._config.add_nodes_url.format(project_id=project_id, cluster_id=cluster_id) + return self._client.request_json("PATCH", url, json=data) + try: from .paas_cc_ext import get_auth_project # noqa diff --git a/bcs-app/backend/container_service/clusters/cluster_manager.py b/bcs-app/backend/container_service/clusters/cluster_manager.py new file mode 100644 index 000000000..4f33b708c --- /dev/null +++ b/bcs-app/backend/container_service/clusters/cluster_manager.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import logging +from typing import Dict, List + +from django.conf import settings + +from backend.components import base as comp_base +from backend.components import paas_cc +from backend.components.bcs_api import cluster +from backend.container_service.clusters.models import CommonStatus + +from . import constants + +logger = logging.getLogger(__name__) + + +def create_cluster(access_token: str, project_id: str, cc_app_id: int, data: Dict) -> Dict: + """创建集群信息""" + bcs_cc_client = paas_cc.PaaSCCClient(comp_base.ComponentAuth(access_token)) + cluster_env = settings.CLUSTER_ENV.get(data["environment"]) + data["environment"] = cluster_env + cluster_data = bcs_cc_client.create_cluster(project_id, data) + + # 写入数据到clustermanager + bcs_basic_config = cluster.BcsBasicConfig( + projectID=project_id, + businessID=str(cc_app_id), + clusterID=cluster_data["cluster_id"], + clusterName=data["name"], + provider="bcs", + environment=cluster_env, + engineType=data["coes"], + onlyCreateInfo=True, + ) + # NOTE: 切换流程后需要适配集群的配置 + cloud_cluster_config = cluster.CloudClusterConfig( + region=data.get("area_id"), + master=data.get("master_ips") or [], + vpcID=data.get("vpc_id"), + clusterBasicSettings={"version": data.get("version", "")}, + clusterAdvanceSettings={ + "containerRuntime": constants.ContainerRuntime.Docker, + "IPVS": True if data.get("kube_proxy_mode") == constants.KubeProxy.IPVS else False, + }, + ) + try: + cluster_config = cluster.ClusterConfig( + creator=data["creator"], bcs_basic_config=bcs_basic_config, cloud_cluster_config=cloud_cluster_config + ) + client = cluster.BcsClusterApiClient(comp_base.ComponentAuth(access_token)) + client.add_cluster(cluster_config) + except Exception as e: + logger.error("add clustermanager cluster failed, %s", e) + return cluster_data + + +def update_cluster(access_token: str, project_id: str, cluster_id: str, data: Dict) -> Dict: + """更新集群""" + bcs_cc_client = paas_cc.PaaSCCClient(comp_base.ComponentAuth(access_token)) + cluster_data = bcs_cc_client.update_cluster(project_id, cluster_id, data) + cluster_config = cluster.UpdatedClusterConfig( + projectID=project_id, + clusterID=cluster_id, + updater=data.get("updater"), + clusterName=data.get("name"), + status=constants.ClusterStatus.RUNNING, + ) + try: + client = cluster.BcsClusterApiClient(comp_base.ComponentAuth(access_token)) + client.update_cluster(cluster_config) + except Exception as e: + logger.error("update clustermanager cluster failed, %s", e) + + return cluster_data + + +def delete_cluster(access_token: str, project_id: str, cluster_id: str) -> Dict: + """删除集群""" + bcs_cc_client = paas_cc.PaaSCCClient(comp_base.ComponentAuth(access_token)) + cluster_data = bcs_cc_client.update_cluster(project_id, cluster_id, {"status": CommonStatus.Removing}) + try: + client = cluster.BcsClusterApiClient(comp_base.ComponentAuth(access_token)) + client.delete_cluster(cluster_id=cluster_id, only_delete_info=True) + except Exception as e: + logger.error("delete clustermanager cluster failed, %s", e) + return cluster_data + + +def add_nodes(access_token: str, project_id: str, cluster_id: str, data: Dict): + """添加节点""" + bcs_cc_client = paas_cc.PaaSCCClient(comp_base.ComponentAuth(access_token)) + bcs_cc_client.add_nodes(project_id, cluster_id, data) + try: + client = cluster.BcsClusterApiClient(comp_base.ComponentAuth(access_token)) + node_config = cluster.NodeConfig(nodes=[node["inner_ip"] for node in data["objects"]], onlyCreateInfo=True) + client.add_nodes(cluster_id, node_config) + except Exception as e: + logger.error("add clustermanager nodes failed, %s", e) + + +def delete_nodes(access_token: str, project_id: str, cluster_id: str, node_ips: List[str]): + """删除节点""" + bcs_cc_client = paas_cc.PaaSCCClient(comp_base.ComponentAuth(access_token)) + data = [paas_cc.UpdateNodesData(inner_ip=ip, status=CommonStatus.Removing) for ip in node_ips] + bcs_cc_client.update_node_list(project_id, cluster_id, data) + try: + client = cluster.BcsClusterApiClient(comp_base.ComponentAuth(access_token)) + client.delete_nodes(cluster_id, node_ips) + except Exception as e: + logger.error("update clustermanager nodes failed, %s", e) diff --git a/bcs-app/backend/container_service/clusters/constants.py b/bcs-app/backend/container_service/clusters/constants.py index 2a590e281..65ca72130 100644 --- a/bcs-app/backend/container_service/clusters/constants.py +++ b/bcs-app/backend/container_service/clusters/constants.py @@ -198,3 +198,21 @@ class KubeProxy(str, StructuredEnum): # k8s cluster master role # 参考rancher中定义nodeRoleMaster="node-role.kubernetes.io/master" K8S_NODE_ROLE_MASTER = "node-role.kubernetes.io/master" + + +class ContainerRuntime(str, StructuredEnum): + """容器运行时""" + + Docker = "docker" + Containerd = "containerd" + + +class ClusterStatus(str, StructuredEnum): + """集群状态""" + + CREATING = EnumField("CREATING", label=_("创建中")) + RUNNING = EnumField("RUNNING", label=_("运行中,标识集群正常")) + DELETING = EnumField("DELETING", label=_("删除中")) + FAILURE = EnumField("FAILURE", label=_("失败")) + INITIALIZATION = EnumField("INITIALIZATION", label=_("初始化")) + DELETED = EnumField("DELETED", label=_("已删除")) diff --git a/bcs-app/backend/container_service/projects/auditor.py b/bcs-app/backend/container_service/projects/auditor.py new file mode 100644 index 000000000..5511a2512 --- /dev/null +++ b/bcs-app/backend/container_service/projects/auditor.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from backend.bcs_web.audit_log.audit.auditors import Auditor +from backend.bcs_web.audit_log.audit.context import AuditContext +from backend.bcs_web.audit_log.constants import ResourceType + + +class ProjectAuditor(Auditor): + def __init__(self, audit_ctx: AuditContext): + super().__init__(audit_ctx) + self.audit_ctx.resource_type = ResourceType.Project diff --git a/bcs-app/backend/container_service/projects/views.py b/bcs-app/backend/container_service/projects/views.py index 210d28f0c..35162a27f 100644 --- a/bcs-app/backend/container_service/projects/views.py +++ b/bcs-app/backend/container_service/projects/views.py @@ -14,19 +14,23 @@ """ import json import logging +from typing import Dict from django.conf import settings from django.utils.translation import ugettext_lazy as _ from rest_framework import permissions, viewsets from rest_framework.renderers import BrowsableAPIRenderer +from rest_framework.request import Request from rest_framework.response import Response from rest_framework.views import APIView from backend.bcs_web.audit_log import client +from backend.bcs_web.audit_log.audit.decorators import log_audit_on_view +from backend.bcs_web.audit_log.constants import ActivityType from backend.bcs_web.constants import bcs_project_cache_key from backend.bcs_web.iam.permissions import ProjectPermission from backend.bcs_web.viewsets import SystemViewSet -from backend.components import paas_cc +from backend.components import bcs_api, paas_cc from backend.container_service.projects import base as Project from backend.container_service.projects.utils import ( get_app_by_user_role, @@ -37,10 +41,10 @@ from backend.utils.cache import region from backend.utils.errcodes import ErrorCode from backend.utils.error_codes import error_codes -from backend.utils.func_controller import get_func_controller from backend.utils.renderers import BKAPIRenderer -from . import serializers +from . import project_manager, serializers +from .auditor import ProjectAuditor from .cmdb import list_biz_maintainers logger = logging.getLogger(__name__) @@ -66,20 +70,6 @@ def deploy_type_list(self, deploy_type): return [] return deploy_type_list - def _register_function_controller(self, func_code, project_list): - enabled, wlist = get_func_controller(func_code) - for project in project_list: - # 黑名单控制 - if project["project_id"] in wlist: - continue - - project["func_wlist"].add(func_code) - - def register_function_controller(self, project_list): - """注册功能白名单""" - for func_code in getattr(settings, "PROJECT_FUNC_CODES", []): - self._register_function_controller(func_code, project_list) - def list(self, request): """获取项目列表""" # 获取已经授权的项目 @@ -101,10 +91,6 @@ def list(self, request): ) info["project_code"] = info["english_name"] info["deploy_type"] = self.deploy_type_list(info.get("deploy_type")) - info["func_wlist"] = set() - - # 白名单用于控制mesos集群是否开启了service monitor组件 - self.register_function_controller(data) return Response(data) @@ -137,9 +123,6 @@ def info(self, request, project_id): # 添加业务名称 data["cc_app_name"] = get_application_name(request) data["can_edit"] = self.can_edit(request, project_id) - # TODO: 待拆分后,可以去掉func_list - data["func_wlist"] = set() - self.register_function_controller([data]) return Response(data) def validate_update_project_data(self, request): @@ -153,6 +136,7 @@ def invalid_project_cache(self, project_id): # NOTE: 后续permission统一后,可以删除下面的缓存标识 region.delete(f"BK_DEVOPS_BCS:HAS_BCS_SERVICE:{project_id}") + @log_audit_on_view(ProjectAuditor, activity_type=ActivityType.Modify) def update(self, request, project_id): """更新项目信息""" if not self.can_edit(request, project_id): @@ -161,21 +145,8 @@ def update(self, request, project_id): access_token = request.user.token.access_token data["updator"] = request.user.username - # 添加操作日志 - ual_client = client.UserActivityLogClient( - project_id=project_id, - user=request.user.username, - resource_type="project", - resource=request.project.project_name, - resource_id=project_id, - description="{}: {}".format(_("更新项目"), request.project.project_name), - ) - resp = paas_cc.update_project_new(access_token, project_id, data) - if resp.get("code") != ErrorCode.NoError: - ual_client.log_modify(activity_status="failed") - raise error_codes.APIError(_("更新项目信息失败,错误详情: {}").format(resp.get("message"))) - ual_client.log_modify(activity_status="succeed") - project_data = resp.get("data") + # 统一更新或创建项目部分,方便bcs cc模块移除后的处理 + project_data = project_manager.update_or_create_project(access_token, project_id, data) if project_data: project_data["created_at"], project_data["updated_at"] = self.normalize_create_update_time( project_data["created_at"], project_data["updated_at"] diff --git a/bcs-app/backend/settings/ce/base.py b/bcs-app/backend/settings/ce/base.py index 172dbc1d0..8e918300a 100644 --- a/bcs-app/backend/settings/ce/base.py +++ b/bcs-app/backend/settings/ce/base.py @@ -236,6 +236,8 @@ def configure_workers(*args, **kwargs): BCS_CLUSTER_ENV_AND_HTTPS_SERVER_HOST = {"prod": os.environ.get("BKAPP_BCS_API_DOMAIN")} # BCS API PRE URL BCS_API_PRE_URL = f"{APIGW_HOST}/api/apigw/bcs_api" +BCS_API_GW_DOMAIN = f"{APIGW_HOST}/api/apigw/bcs-api-gateway" +BCS_API_GW_ENV = "prod" BK_SSM_HOST = os.environ.get("BKAPP_SSM_HOST") diff --git a/bcs-app/backend/tests/components/bcs_api/test_cluster.py b/bcs-app/backend/tests/components/bcs_api/test_cluster.py new file mode 100644 index 000000000..b2277d5c5 --- /dev/null +++ b/bcs-app/backend/tests/components/bcs_api/test_cluster.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from requests_mock import ANY + +from backend.components.base import ComponentAuth +from backend.components.bcs_api import cluster + +SUCCESS_CODE = 0 + + +class TestBcsApiClient: + def test_add_cluster(self, project_id, cluster_id, request_user, random_name, requests_mock): + expected_data = {"cluster_id": cluster_id} + expected_task = {"taskID": random_name} + requests_mock.post(ANY, json={"code": SUCCESS_CODE, "data": expected_data, "task": expected_task}) + + client = cluster.BcsClusterApiClient(ComponentAuth(request_user.token.access_token)) + cloud_cluster_config = cluster.CloudClusterConfig( + region=random_name, manageType="INDEPENDENT_CLUSTER", master=["fake_ip"], vpcID=random_name, cloudID=1 + ) + bcs_basic_config = cluster.BcsBasicConfig( + projectID=project_id, + businessID=1, + clusterID=cluster_id, + clusterName=random_name, + provider="", + environment="test", + engineType="k8s", + isExclusive=False, + clusterType="k8s", + federationClusterID="", + labels={}, + onlyCreateInfo=True, + bcsAddons={}, + extraAddons={}, + ) + cluster_config = cluster.ClusterConfig( + creator=request_user.username, + cloud_cluster_config=cloud_cluster_config, + bcs_basic_config=bcs_basic_config, + ) + resp = client.add_cluster(cluster_config) + assert resp["task"] == expected_task + assert resp["data"] == expected_data + + def test_update_cluster(self, project_id, cluster_id, request_user, random_name, requests_mock): + expected_data = {"cluster_id": cluster_id} + requests_mock.put(ANY, json={"code": SUCCESS_CODE, "data": expected_data}) + + client = cluster.BcsClusterApiClient(ComponentAuth(request_user.token.access_token)) + cluster_config = cluster.UpdatedClusterConfig( + projectID=project_id, + clusterID=cluster_id, + updater=request_user.username, + clusterName="test", + status="RUNNING", + ) + resp = client.update_cluster(cluster_config) + assert resp == expected_data + + def test_delete_cluster(self, cluster_id, random_name, request_user, requests_mock): + expected_task = {"taskID": random_name} + requests_mock.delete(ANY, json={"code": SUCCESS_CODE, "data": {}, "task": expected_task}) + + client = cluster.BcsClusterApiClient(ComponentAuth(request_user.token.access_token)) + resp = client.delete_cluster(cluster_id) + assert resp["task"] == expected_task + + def test_query_task(self, random_name, request_user, requests_mock): + expected_data = {"taskID": random_name} + requests_mock.get(ANY, json={"code": SUCCESS_CODE, "data": expected_data}) + + client = cluster.BcsClusterApiClient(ComponentAuth(request_user.token.access_token)) + resp = client.query_task(random_name) + assert resp["data"] == expected_data diff --git a/bcs-app/backend/tests/components/bcs_api/test_project.py b/bcs-app/backend/tests/components/bcs_api/test_project.py new file mode 100644 index 000000000..306bbced6 --- /dev/null +++ b/bcs-app/backend/tests/components/bcs_api/test_project.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from requests_mock import ANY + +from backend.components.base import ComponentAuth +from backend.components.bcs_api import project + +SUCCESS_CODE = 0 + + +class TestBcsApiProjectClient: + def test_query_project(self, project_id, request_user, requests_mock): + expected_data = {"project_id": project_id} + requests_mock.get(ANY, json={"code": SUCCESS_CODE, "data": expected_data}) + + client = project.BcsProjectApiClient(ComponentAuth(request_user.token.access_token)) + resp = client.query_project(project_id) + assert resp == expected_data + + def test_create_project(self, project_id, request_user, random_name, requests_mock): + requests_mock.post(ANY, json={"code": SUCCESS_CODE, "message": "success"}) + + client = project.BcsProjectApiClient(ComponentAuth(request_user.token.access_token)) + basic_config = project.ProjectBasicConfig( + projectID=project_id, name=random_name, englishName=random_name, kind="k8s", businessID="1", description="" + ) + reserved_config = project.ProjectReservedConfig() + project_config = project.ProjectConfig( + creator=request_user.username, basic_config=basic_config, reserved_config=reserved_config + ) + resp = client.create_project(project_config) + assert resp["code"] == SUCCESS_CODE + + def test_update_project(self, project_id, request_user, random_name, requests_mock): + expected_data = {"project_id": project_id} + requests_mock.put(ANY, json={"code": SUCCESS_CODE, "data": expected_data}) + + client = project.BcsProjectApiClient(ComponentAuth(request_user.token.access_token)) + project_config = project.UpdatedProjectConfig( + projectID=project_id, + updater=request_user.username, + name=random_name, + kind="k8s", + businessID=1, + ) + resp = client.update_project(project_config) + assert resp["code"] == SUCCESS_CODE diff --git a/bcs-app/backend/tests/components/test_bcs_api.py b/bcs-app/backend/tests/components/test_bcs_api.py index 6eebf7d9c..47f5ee98d 100644 --- a/bcs-app/backend/tests/components/test_bcs_api.py +++ b/bcs-app/backend/tests/components/test_bcs_api.py @@ -5,9 +5,7 @@ Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://opensource.org/licenses/MIT - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. @@ -15,6 +13,7 @@ import pytest from requests_mock import ANY +from backend.components import bcs_api from backend.components.base import ComponentAuth from backend.components.bcs_api import BcsApiClient diff --git a/bcs-app/backend/tests/container_service/clusters/test_cluster_manager.py b/bcs-app/backend/tests/container_service/clusters/test_cluster_manager.py new file mode 100644 index 000000000..3b1c50c83 --- /dev/null +++ b/bcs-app/backend/tests/container_service/clusters/test_cluster_manager.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from unittest.mock import patch + +import pytest + +from backend.container_service.clusters import cluster_manager +from backend.container_service.clusters.models import CommonStatus +from backend.tests.testing_utils.mocks import paas_cc +from backend.tests.testing_utils.mocks.bcs_api import cluster + +fake_biz_id = 1 +fake_coes = "k8s" +fake_cluster_name = "test-cluster" +fake_cluster_data = { + "creator": "admin", + "environment": "test", + "name": fake_cluster_name, + "coes": fake_coes, + "area_id": 1, + "version": "1.12", +} + + +@pytest.fixture(autouse=True) +def pre_patch(): + with patch( + "backend.container_service.clusters.cluster_manager.paas_cc.PaaSCCClient", new=paas_cc.StubPaaSCCClient + ), patch( + "backend.container_service.clusters.cluster_manager.cluster.BcsClusterApiClient", + new=cluster.StubBcsClusterApiClient, + ): + yield + + +def test_create_cluster(project_id, cluster_id, request_user): + cluster_data = cluster_manager.create_cluster( + request_user.token.access_token, project_id, fake_biz_id, fake_cluster_data + ) + assert cluster_data["type"] == fake_coes + + +def test_update_cluster(project_id, cluster_id, request_user): + cluster_data = cluster_manager.update_cluster( + request_user.token.access_token, project_id, cluster_id, {"name": fake_cluster_name} + ) + assert cluster_data["name"] == fake_cluster_name + + +def test_delete_cluster(project_id, cluster_id, request_user): + cluster_data = cluster_manager.delete_cluster(request_user.token.access_token, project_id, cluster_id) + assert cluster_data["status"] == CommonStatus.Removing diff --git a/bcs-app/backend/tests/container_service/projects/test_project_manager.py b/bcs-app/backend/tests/container_service/projects/test_project_manager.py new file mode 100644 index 000000000..f7343a314 --- /dev/null +++ b/bcs-app/backend/tests/container_service/projects/test_project_manager.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from unittest.mock import patch + +from backend.container_service.projects.project_manager import update_or_create_project +from backend.tests.testing_utils.mocks import paas_cc +from backend.tests.testing_utils.mocks.bcs_api import project + +fake_project_kind = 1 +fake_project_data = {"updator": "admin", "kind": fake_project_kind, "cc_app_id": 1} + + +@patch("backend.container_service.projects.project_manager.paas_cc.PaaSCCClient", new=paas_cc.StubPaaSCCClient) +@patch( + "backend.container_service.projects.project_manager.project.BcsProjectApiClient", + new=project.StubBcsProjectApiClient, +) +def test_update_project(request_user, project_id): + """更新项目信息 + 项目已经存在于clustermanager中 + """ + project_data = update_or_create_project(request_user.token.access_token, project_id, fake_project_data) + assert project_data["kind"] == fake_project_kind + + +@patch("backend.container_service.projects.project_manager.paas_cc.PaaSCCClient", new=paas_cc.StubPaaSCCClient) +@patch( + "backend.container_service.projects.project_manager.project.BcsProjectApiClient.create_project", + new=project.StubBcsProjectApiClient().create_project, +) +@patch( + "backend.container_service.projects.project_manager.project.BcsProjectApiClient.update_project", + new=project.StubBcsProjectApiClient().update_project_not_exist, +) +def test_create_project(request_user, project_id): + project_data = update_or_create_project(request_user.token.access_token, project_id, fake_project_data) + assert project_data["kind"] == fake_project_kind diff --git a/bcs-app/backend/tests/testing_utils/mocks/bcs_api.py b/bcs-app/backend/tests/testing_utils/mocks/bcs_api/__init__.py similarity index 97% rename from bcs-app/backend/tests/testing_utils/mocks/bcs_api.py rename to bcs-app/backend/tests/testing_utils/mocks/bcs_api/__init__.py index 4fe02257b..b2562d0a5 100644 --- a/bcs-app/backend/tests/testing_utils/mocks/bcs_api.py +++ b/bcs-app/backend/tests/testing_utils/mocks/bcs_api/__init__.py @@ -14,7 +14,7 @@ """ from typing import Dict -from .utils import mockable_function +from ..utils import mockable_function class StubBcsApiClient: diff --git a/bcs-app/backend/tests/testing_utils/mocks/bcs_api/cluster.py b/bcs-app/backend/tests/testing_utils/mocks/bcs_api/cluster.py new file mode 100644 index 000000000..03bc41828 --- /dev/null +++ b/bcs-app/backend/tests/testing_utils/mocks/bcs_api/cluster.py @@ -0,0 +1,856 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from typing import Dict, List + +from backend.components.bcs_api import cluster + + +class StubBcsClusterApiClient: + """使用假数据的 BCS cluster Api client 对象""" + + def __init__(self, *args, **kwargs): + pass + + def add_cluster(self, *args, **kwargs) -> Dict: + return {"code": 0, "data": self.make_add_cluster_resp()} + + def update_cluster(self, cluster_config: cluster.UpdatedClusterConfig) -> Dict: + return self.make_update_cluster_data() + + def delete_cluster( + self, cluster_id: str, is_force: bool = False, is_clean_resource=True, only_delete_info=False + ) -> Dict: + return {"code": 0, "data": self.make_delete_cluster_resp(cluster_id)} + + def add_nodes(self, cluster_id: str, node_config: cluster.NodeConfig) -> Dict: + return self.make_add_nodes_data() + + def delete_nodes(self, cluster_id: str, nodes: List[str], delete_mode: str, is_force: bool = False) -> Dict: + return self.make_delete_nodes_data() + + def query_task(self, task_id: str) -> Dict: + return self.make_query_task_data(task_id) + + @staticmethod + def make_add_cluster_resp() -> Dict: + return { + "code": 0, + "message": "string", + "result": True, + "data": { + "clusterID": "string", + "clusterName": "string", + "federationClusterID": "string", + "provider": "string", + "region": "string", + "vpcID": "string", + "projectID": "string", + "businessID": "string", + "environment": "string", + "engineType": "string", + "isExclusive": True, + "clusterType": "string", + "labels": {"additionalProp1": "string", "additionalProp2": "string", "additionalProp3": "string"}, + "creator": "string", + "createTime": "string", + "updateTime": "string", + "bcsAddons": { + "additionalProp1": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp2": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp3": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + }, + "extraAddons": { + "additionalProp1": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp2": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp3": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + }, + "systemID": "string", + "manageType": "string", + "master": { + "additionalProp1": { + "nodeID": "string", + "innerIP": "string", + "instanceType": "string", + "CPU": 0, + "mem": 0, + "GPU": 0, + "status": "string", + "zoneID": "string", + "nodeGroupID": "string", + "clusterID": "string", + }, + "additionalProp2": { + "nodeID": "string", + "innerIP": "string", + "instanceType": "string", + "CPU": 0, + "mem": 0, + "GPU": 0, + "status": "string", + "zoneID": "string", + "nodeGroupID": "string", + "clusterID": "string", + }, + "additionalProp3": { + "nodeID": "string", + "innerIP": "string", + "instanceType": "string", + "CPU": 0, + "mem": 0, + "GPU": 0, + "status": "string", + "zoneID": "string", + "nodeGroupID": "string", + "clusterID": "string", + }, + }, + "networkSettings": { + "clusterIPv4CIDR": "string", + "serviceIPv4CIDR": "string", + "maxNodePodNum": "string", + "maxServiceNum": "string", + }, + "clusterBasicSettings": { + "OS": "string", + "version": "string", + "clusterTags": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "clusterAdvanceSettings": { + "IPVS": True, + "containerRuntime": "string", + "runtimeVersion": "string", + "extraArgs": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "nodeSettings": {"dockerGraphPath": "string", "mountTarget": "string"}, + "status": "string", + "updator": "string", + }, + "task": { + "taskID": "string", + "taskType": "string", + "status": "string", + "message": "string", + "start": "string", + "end": "string", + "executionTime": 0, + "currentStep": "string", + "stepSequence": ["string"], + "steps": { + "additionalProp1": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp2": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp3": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + }, + "clusterID": "string", + "projectID": "string", + "creator": "string", + "lastUpdate": "string", + "updator": "string", + "forceTerminate": True, + }, + } + + @staticmethod + def make_update_cluster_data() -> Dict: + { + "clusterID": "string", + "clusterName": "string", + "federationClusterID": "string", + "provider": "string", + "region": "string", + "vpcID": "string", + "projectID": "string", + "businessID": "string", + "environment": "string", + "engineType": "string", + "isExclusive": True, + "clusterType": "string", + "labels": {"additionalProp1": "string", "additionalProp2": "string", "additionalProp3": "string"}, + "creator": "string", + "createTime": "string", + "updateTime": "string", + "bcsAddons": { + "additionalProp1": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp2": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp3": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + }, + "extraAddons": { + "additionalProp1": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp2": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp3": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + }, + "systemID": "string", + "manageType": "string", + "master": { + "additionalProp1": { + "nodeID": "string", + "innerIP": "string", + "instanceType": "string", + "CPU": 0, + "mem": 0, + "GPU": 0, + "status": "string", + "zoneID": "string", + "nodeGroupID": "string", + "clusterID": "string", + }, + "additionalProp2": { + "nodeID": "string", + "innerIP": "string", + "instanceType": "string", + "CPU": 0, + "mem": 0, + "GPU": 0, + "status": "string", + "zoneID": "string", + "nodeGroupID": "string", + "clusterID": "string", + }, + "additionalProp3": { + "nodeID": "string", + "innerIP": "string", + "instanceType": "string", + "CPU": 0, + "mem": 0, + "GPU": 0, + "status": "string", + "zoneID": "string", + "nodeGroupID": "string", + "clusterID": "string", + }, + }, + "networkSettings": { + "clusterIPv4CIDR": "string", + "serviceIPv4CIDR": "string", + "maxNodePodNum": "string", + "maxServiceNum": "string", + }, + "clusterBasicSettings": { + "OS": "string", + "version": "string", + "clusterTags": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "clusterAdvanceSettings": { + "IPVS": True, + "containerRuntime": "string", + "runtimeVersion": "string", + "extraArgs": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "nodeSettings": {"dockerGraphPath": "string", "mountTarget": "string"}, + "status": "string", + "updator": "string", + } + + @staticmethod + def make_delete_cluster_resp(cluster_id: str) -> Dict: + return { + "code": 0, + "message": "string", + "result": True, + "data": { + "clusterID": cluster_id, + "clusterName": "string", + "federationClusterID": "string", + "provider": "string", + "region": "string", + "vpcID": "string", + "projectID": "string", + "businessID": "string", + "environment": "string", + "engineType": "string", + "isExclusive": True, + "clusterType": "string", + "labels": {"additionalProp1": "string", "additionalProp2": "string", "additionalProp3": "string"}, + "creator": "string", + "createTime": "string", + "updateTime": "string", + "bcsAddons": { + "additionalProp1": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp2": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp3": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + }, + "extraAddons": { + "additionalProp1": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp2": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "additionalProp3": { + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + }, + "systemID": "string", + "manageType": "string", + "master": { + "additionalProp1": { + "nodeID": "string", + "innerIP": "string", + "instanceType": "string", + "CPU": 0, + "mem": 0, + "GPU": 0, + "status": "string", + "zoneID": "string", + "nodeGroupID": "string", + "clusterID": "string", + }, + "additionalProp2": { + "nodeID": "string", + "innerIP": "string", + "instanceType": "string", + "CPU": 0, + "mem": 0, + "GPU": 0, + "status": "string", + "zoneID": "string", + "nodeGroupID": "string", + "clusterID": "string", + }, + "additionalProp3": { + "nodeID": "string", + "innerIP": "string", + "instanceType": "string", + "CPU": 0, + "mem": 0, + "GPU": 0, + "status": "string", + "zoneID": "string", + "nodeGroupID": "string", + "clusterID": "string", + }, + }, + "networkSettings": { + "clusterIPv4CIDR": "string", + "serviceIPv4CIDR": "string", + "maxNodePodNum": "string", + "maxServiceNum": "string", + }, + "clusterBasicSettings": { + "OS": "string", + "version": "string", + "clusterTags": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "clusterAdvanceSettings": { + "IPVS": True, + "containerRuntime": "string", + "runtimeVersion": "string", + "extraArgs": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + }, + "nodeSettings": {"dockerGraphPath": "string", "mountTarget": "string"}, + "status": "string", + "updator": "string", + }, + "tasks": [ + { + "taskID": "string", + "taskType": "string", + "status": "string", + "message": "string", + "start": "string", + "end": "string", + "executionTime": 0, + "currentStep": "string", + "stepSequence": ["string"], + "steps": { + "additionalProp1": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp2": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp3": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + }, + "clusterID": "string", + "projectID": "string", + "creator": "string", + "lastUpdate": "string", + "updator": "string", + "forceTerminate": True, + } + ], + } + + @staticmethod + def make_add_nodes_data() -> Dict: + return { + "taskID": "string", + "taskType": "string", + "status": "string", + "message": "string", + "start": "string", + "end": "string", + "executionTime": 0, + "currentStep": "string", + "stepSequence": ["string"], + "steps": { + "additionalProp1": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp2": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp3": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + }, + "clusterID": "string", + "projectID": "string", + "creator": "string", + "lastUpdate": "string", + "updator": "string", + "forceTerminate": True, + } + + @staticmethod + def make_delete_nodes_data() -> Dict: + return { + "taskID": "string", + "taskType": "string", + "status": "string", + "message": "string", + "start": "string", + "end": "string", + "executionTime": 0, + "currentStep": "string", + "stepSequence": ["string"], + "steps": { + "additionalProp1": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp2": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp3": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + }, + "clusterID": "string", + "projectID": "string", + "creator": "string", + "lastUpdate": "string", + "updator": "string", + "forceTerminate": True, + } + + @staticmethod + def make_query_task_data(task_id: str) -> Dict: + return { + "taskID": task_id, + "taskType": "string", + "status": "string", + "message": "string", + "start": "string", + "end": "string", + "executionTime": 0, + "currentStep": "string", + "stepSequence": ["string"], + "steps": { + "additionalProp1": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp2": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + "additionalProp3": { + "name": "string", + "system": "string", + "link": "string", + "params": { + "additionalProp1": "string", + "additionalProp2": "string", + "additionalProp3": "string", + }, + "retry": 0, + "start": "string", + "end": "string", + "executionTime": 0, + "status": "string", + "message": "string", + "lastUpdate": "string", + }, + }, + "clusterID": "string", + "projectID": "string", + "creator": "string", + "lastUpdate": "string", + "updator": "string", + "forceTerminate": True, + } diff --git a/bcs-app/backend/tests/testing_utils/mocks/bcs_api/project.py b/bcs-app/backend/tests/testing_utils/mocks/bcs_api/project.py new file mode 100644 index 000000000..477e35214 --- /dev/null +++ b/bcs-app/backend/tests/testing_utils/mocks/bcs_api/project.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +from typing import Dict + +from backend.components.bcs_api.constants import RECORD_NOT_EXIST_CODE + + +class StubBcsProjectApiClient: + """使用假数据的 BCS project Api client 对象""" + + def __init__(self, *args, **kwargs): + pass + + def create_project(self, *args, **kwargs) -> Dict: + return {"code": 0, "data": self.make_project_data()} + + def query_project(self, project_id) -> Dict: + return self.make_project_data() + + def update_project(self, *args, **kwargs) -> Dict: + return {"code": 0, "data": self.make_project_data()} + + def update_project_not_exist(self, *args, **kwargs) -> Dict: + return {"code": RECORD_NOT_EXIST_CODE} + + @staticmethod + def make_project_data() -> Dict: + return { + "projectID": "string", + "name": "string", + "englishName": "string", + "creator": "string", + "updater": "string", + "projectType": 0, + "useBKRes": True, + "description": "string", + "isOffline": True, + "kind": "string", + "businessID": "string", + "deployType": 0, + "bgID": "string", + "bgName": "string", + "deptID": "string", + "deptName": "string", + "centerID": "string", + "centerName": "string", + "isSecret": True, + "credentials": { + "additionalProp1": {"key": "string", "secret": "string"}, + "additionalProp2": {"key": "string", "secret": "string"}, + "additionalProp3": {"key": "string", "secret": "string"}, + }, + "createTime": "string", + "updateTime": "string", + } diff --git a/bcs-app/backend/tests/testing_utils/mocks/paas_cc.py b/bcs-app/backend/tests/testing_utils/mocks/paas_cc.py index dad75352f..07802e1fd 100644 --- a/bcs-app/backend/tests/testing_utils/mocks/paas_cc.py +++ b/bcs-app/backend/tests/testing_utils/mocks/paas_cc.py @@ -13,9 +13,7 @@ specific language governing permissions and limitations under the License. """ import uuid -from typing import Dict - -from backend.container_service.projects.base.constants import ProjectKind +from typing import Dict, List from .utils import mockable_function @@ -34,17 +32,30 @@ def get_cluster(self, project_id: str, cluster_id: str) -> Dict: def get_project(self, project_id: str) -> Dict: return self.make_project_data(project_id) - @mockable_function - def get_mesos_project(self, project_id: str) -> Dict: - """返回mesos项目信息""" - data = self.make_project_data(project_id) - data["kind"] = ProjectKind.MESOS.value - return data - @mockable_function def get_cluster_namespace_list(self, project_id: str, cluster_id: str) -> Dict: return self.make_cluster_namespace_data(project_id, cluster_id) + @mockable_function + def update_project(self, project_id: str, *args, **kwargs) -> Dict: + return self.make_project_data(project_id) + + @mockable_function + def create_cluster(self, project_id: str, cluster_id: str, *args, **kwargs) -> Dict: + return self.make_cluster_data(project_id, cluster_id) + + @mockable_function + def update_cluster(self, project_id: str, cluster_id: str, *args, **kwargs) -> Dict: + return self.make_cluster_data(project_id, cluster_id, *args, **kwargs) + + @mockable_function + def add_nodes(self, project_id: str, cluster_id: str) -> List: + return self.make_nodes_data(project_id, cluster_id) + + @mockable_function + def update_node_list(self, project_id: str, cluster_id: str) -> List: + return self.make_nodes_data(project_id, cluster_id) + @staticmethod def wrap_resp(data): return { @@ -56,7 +67,8 @@ def wrap_resp(data): } @staticmethod - def make_cluster_data(project_id: str, cluster_id: str): + def make_cluster_data(project_id: str, cluster_id: str, *args, **kwargs): + status = args[0]["status"] if args and "status" in args[0] else "normal" _stub_time = '2021-01-01T00:00:00+08:00' return { 'area_id': 1, @@ -81,7 +93,7 @@ def make_cluster_data(project_id: str, cluster_id: str): 'remain_cpu': 10, 'remain_disk': 0, 'remain_mem': 10, - 'status': 'normal', + 'status': status, 'total_cpu': 12, 'total_disk': 0, 'total_mem': 64, @@ -91,7 +103,6 @@ def make_cluster_data(project_id: str, cluster_id: str): @staticmethod def make_project_data(project_id: str): - _stub_time = '2021-01-01T00:00:00+08:00' return { "approval_status": 2, "approval_time": "2020-01-01T00:00:00+08:00", @@ -145,3 +156,7 @@ def make_cluster_namespace_data(project_id: str, cluster_id: str) -> Dict: } ], } + + @staticmethod + def make_nodes_data(project_id: str, cluster_id: str) -> List: + return [{"id": 1, "inner_ip": "127.0.0.1", "project_id": project_id, "cluster_id": cluster_id}] From 775aca023ee85c6c48e6a37f1020229c51d796d2 Mon Sep 17 00:00:00 2001 From: bellkeyang Date: Tue, 14 Sep 2021 19:57:22 +0800 Subject: [PATCH 04/10] feat: write data to cluster manager --- .../backend/components/bcs_api/__init__.py | 3 + .../container_service/clusters/test_cm.py | 59 ------- .../container_service/projects/test_cm.py | 45 ----- .../tests/testing_utils/mocks/bk_cc.py | 158 ++---------------- 4 files changed, 16 insertions(+), 249 deletions(-) delete mode 100644 bcs-app/backend/tests/container_service/clusters/test_cm.py delete mode 100644 bcs-app/backend/tests/container_service/projects/test_cm.py diff --git a/bcs-app/backend/components/bcs_api/__init__.py b/bcs-app/backend/components/bcs_api/__init__.py index b905fa28d..d2c320661 100644 --- a/bcs-app/backend/components/bcs_api/__init__.py +++ b/bcs-app/backend/components/bcs_api/__init__.py @@ -51,9 +51,12 @@ def __call__(self, r: PreparedRequest): class BcsApiClient(BkApiClient): """访问 BCS API 服务的 Client 对象 + :param auth: 包含校验信息的对象 + API 方法常用请求参数说明 === + :param env_name: 集群环境,比如 stag/prod :param project_id: 项目 ID :param cluster_id: 集群 ID diff --git a/bcs-app/backend/tests/container_service/clusters/test_cm.py b/bcs-app/backend/tests/container_service/clusters/test_cm.py deleted file mode 100644 index f44f6767d..000000000 --- a/bcs-app/backend/tests/container_service/clusters/test_cm.py +++ /dev/null @@ -1,59 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community -Edition) available. -Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. -Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://opensource.org/licenses/MIT - -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on -an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. -""" -from unittest.mock import patch - -import pytest - -from backend.container_service.clusters import cm, constants -from backend.container_service.clusters.models import CommonStatus -from backend.tests.testing_utils.base import generate_random_string -from backend.tests.testing_utils.mocks import bcs_api, paas_cc - -fake_biz_id = 1 -fake_coes = "k8s" -fake_cluster_name = "test-cluster" -fake_cluster_data = { - "creator": "admin", - "environment": "test", - "name": fake_cluster_name, - "coes": fake_coes, - "area_id": 1, - "version": "1.12", -} - - -@pytest.fixture(autouse=True) -def pre_patch(): - with patch("backend.container_service.clusters.cm.paas_cc.PaaSCCClient", new=paas_cc.StubPaaSCCClient), patch( - "backend.container_service.clusters.cm.bcs_api.BcsApiClient", new=bcs_api.StubBcsApiClient - ): - yield - - -def test_create_cluster(project_id, cluster_id, request_user): - cluster_data = cm.create_cluster(request_user.token.access_token, project_id, fake_biz_id, fake_cluster_data) - assert cluster_data["type"] == fake_coes - - -def test_update_cluster(project_id, cluster_id, request_user): - cluster_data = cm.update_cluster( - request_user.token.access_token, project_id, cluster_id, {"name": fake_cluster_name} - ) - assert cluster_data["name"] == fake_cluster_name - - -def test_delete_cluster(project_id, cluster_id, request_user): - cluster_data = cm.delete_cluster(request_user.token.access_token, project_id, cluster_id) - assert cluster_data["status"] == CommonStatus.Removing diff --git a/bcs-app/backend/tests/container_service/projects/test_cm.py b/bcs-app/backend/tests/container_service/projects/test_cm.py deleted file mode 100644 index 6a6744de3..000000000 --- a/bcs-app/backend/tests/container_service/projects/test_cm.py +++ /dev/null @@ -1,45 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community -Edition) available. -Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. -Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://opensource.org/licenses/MIT - -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on -an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. -""" -from unittest.mock import patch - -from backend.container_service.projects.cm import update_or_create_project -from backend.tests.testing_utils.mocks import bcs_api, paas_cc - -fake_project_kind = 1 -fake_project_data = {"updator": "admin", "kind": fake_project_kind, "cc_app_id": 1} - - -@patch("backend.container_service.projects.cm.paas_cc.PaaSCCClient", new=paas_cc.StubPaaSCCClient) -@patch("backend.container_service.projects.cm.bcs_api.BcsApiClient", new=bcs_api.StubBcsApiClient) -def test_update_project(request_user, project_id): - """更新项目信息 - 项目已经存在于clustermanager中 - """ - project_data = update_or_create_project(request_user.token.access_token, project_id, fake_project_data) - assert project_data["kind"] == fake_project_kind - - -@patch("backend.container_service.projects.cm.paas_cc.PaaSCCClient", new=paas_cc.StubPaaSCCClient) -@patch( - "backend.container_service.projects.cm.bcs_api.BcsApiClient.create_project", - new=bcs_api.StubBcsApiClient().create_project, -) -@patch( - "backend.container_service.projects.cm.bcs_api.BcsApiClient.update_project", - new=bcs_api.StubBcsApiClient().update_project_not_exist, -) -def test_create_project(request_user, project_id): - project_data = update_or_create_project(request_user.token.access_token, project_id, fake_project_data) - assert project_data["kind"] == fake_project_kind diff --git a/bcs-app/backend/tests/testing_utils/mocks/bk_cc.py b/bcs-app/backend/tests/testing_utils/mocks/bk_cc.py index 94f3235c1..3da1b4308 100644 --- a/bcs-app/backend/tests/testing_utils/mocks/bk_cc.py +++ b/bcs-app/backend/tests/testing_utils/mocks/bk_cc.py @@ -12,160 +12,28 @@ an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ -import uuid -from typing import Dict, List +from typing import Dict, List, Optional -from backend.container_service.projects.base.constants import ProjectKind +from backend.components.cc import PageData -from .utils import mockable_function - - -class StubPaaSCCClient: - """使用假数据的 PaaSCCClient 对象""" +class FakeBkCCClient: def __init__(self, *args, **kwargs): pass - @mockable_function - def get_cluster(self, project_id: str, cluster_id: str) -> Dict: - return self.wrap_resp(self.make_cluster_data(project_id, cluster_id)) - - @mockable_function - def get_project(self, project_id: str) -> Dict: - return self.make_project_data(project_id) - - @mockable_function - def get_mesos_project(self, project_id: str) -> Dict: - """返回mesos项目信息""" - data = self.make_project_data(project_id) - data["kind"] = ProjectKind.MESOS.value - return data - - @mockable_function - def get_cluster_namespace_list(self, project_id: str, cluster_id: str) -> Dict: - return self.make_cluster_namespace_data(project_id, cluster_id) - - @mockable_function - def update_project(self, project_id: str, *args, **kwargs) -> Dict: - return self.make_project_data(project_id) - - @mockable_function - def create_cluster(self, project_id: str, cluster_id: str, *args, **kwargs) -> Dict: - return self.make_cluster_data(project_id, cluster_id) - - @mockable_function - def update_cluster(self, project_id: str, cluster_id: str, *args, **kwargs) -> Dict: - return self.make_cluster_data(project_id, cluster_id) - - @mockable_function - def add_nodes(self, project_id: str, cluster_id: str) -> List: - return self.make_nodes_data(project_id, cluster_id) - - @mockable_function - def update_node_list(self, project_id: str, cluster_id: str) -> List: - return self.make_nodes_data(project_id, cluster_id) - - @staticmethod - def wrap_resp(data): - return { - 'code': 0, - 'data': data, - 'message': '', - 'request_id': uuid.uuid4().hex, - 'result': True, - } - - @staticmethod - def make_cluster_data(project_id: str, cluster_id: str): - _stub_time = '2021-01-01T00:00:00+08:00' - return { - 'area_id': 1, - 'artifactory': '', - 'capacity_updated_at': _stub_time, - 'cluster_id': cluster_id, - 'cluster_num': 1, - 'config_svr_count': 0, - 'created_at': _stub_time, - 'creator': 'unknown', - 'description': 'cluster description', - 'disabled': False, - 'environment': 'stag', - 'extra_cluster_id': '', - 'ip_resource_total': 0, - 'ip_resource_used': 0, - 'master_count': 0, - 'name': 'test-cluster', - 'need_nat': True, - 'node_count': 1, - 'project_id': project_id, - 'remain_cpu': 10, - 'remain_disk': 0, - 'remain_mem': 10, - 'status': 'normal', - 'total_cpu': 12, - 'total_disk': 0, - 'total_mem': 64, - 'type': 'k8s', - 'updated_at': _stub_time, - } - - @staticmethod - def make_project_data(project_id: str): - _stub_time = '2021-01-01T00:00:00+08:00' - return { - "approval_status": 2, - "approval_time": "2020-01-01T00:00:00+08:00", - "approver": "", - "bg_id": -1, - "bg_name": "", - "cc_app_id": 100, - "center_id": 100, - "center_name": "", - "created_at": "2020-01-01 00:00:00", - "creator": "unknown", - "data_id": 0, - "deploy_type": "null", - "dept_id": -1, - "dept_name": "", - "description": "", - "english_name": "unittest-cluster", - "extra": {}, - "is_offlined": False, - "is_secrecy": False, - "kind": 1, - "logo_addr": "", - "project_id": project_id, - "project_name": "unittest-cluster", - "project_type": 1, - "remark": "", - "updated_at": "2020-01-01 00:00:00", - "use_bk": False, - "cc_app_name": "demo-app", - "can_edit": False, - } - - @staticmethod - def make_cluster_namespace_data(project_id: str, cluster_id: str) -> Dict: - _stub_time = '2021-06-30T11:13:00+08:00' + def search_biz(self, page: PageData, fields: Optional[List] = None, condition: Optional[Dict] = None) -> Dict: return { "count": 1, - "results": [ + "info": [ { - "cluster_id": cluster_id, - "created_at": _stub_time, - "creator": "admin", - "description": "", - "env_type": "dev", - "has_image_secret": True, - "id": 1, - "name": "default", - "project_id": project_id, - "status": "", - "updated_at": _stub_time, + "bs2_name_id": 1, + "bk_oper_plan": "admin", + "bk_biz_developer": "admin", + "bk_biz_maintainer": "admin", + "bk_dept_name_id": 1, + "bk_biz_name": "demo", + "bk_product_name": "demo", + "default": 0, } ], } - - @staticmethod - def make_nodes_data(project_id: str, cluster_id: str) -> List: - return [{"id": 1, "inner_ip": "127.0.0.1", "project_id": project_id, "cluster_id": cluster_id}] From bfca55cdb79c051b6426d317dd51f533824dad0a Mon Sep 17 00:00:00 2001 From: bellkeyang Date: Tue, 14 Sep 2021 20:12:15 +0800 Subject: [PATCH 05/10] feat: write data to cluster manager --- .../projects/project_manager.py | 101 ++++++++++++++++++ .../tests/components/bcs_api/__init__.py | 0 2 files changed, 101 insertions(+) create mode 100644 bcs-app/backend/container_service/projects/project_manager.py create mode 100644 bcs-app/backend/tests/components/bcs_api/__init__.py diff --git a/bcs-app/backend/container_service/projects/project_manager.py b/bcs-app/backend/container_service/projects/project_manager.py new file mode 100644 index 000000000..fd545b489 --- /dev/null +++ b/bcs-app/backend/container_service/projects/project_manager.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://opensource.org/licenses/MIT + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import logging +from typing import Dict + +from backend.components import base as comp_base +from backend.components import paas_cc +from backend.components.bcs_api import constants, project +from backend.utils.error_codes import error_codes + +from .base.constants import ProjectKindName + +logger = logging.getLogger(__name__) + + +def create_project(access_token: str, project_config: project.ProjectConfig) -> Dict: + """创建cluster manager中项目信息""" + try: + client = project.BcsProjectApiClient(comp_base.ComponentAuth(access_token)) + resp = client.create_project(project_config) + except Exception as e: + raise error_codes.APIError(f"create clustermanager project error, {e}") + return resp + + +def update_project(access_token: str, project_config: project.UpdatedProjectConfig) -> Dict: + """更新cluster manager中项目信息""" + try: + client = project.BcsProjectApiClient(comp_base.ComponentAuth(access_token)) + resp = client.update_project(project_config) + except Exception as e: + raise error_codes.APIError(f"update clustermanager project error, {e}") + return resp + + +def update_or_create_project(access_token: str, project_id: str, data: Dict) -> Dict: + """创建cluster manager所属的项目信息 + NOTE: 当调用接口出现异常后,现阶段不抛出异常;待下线bcs cc模块时,调整为必选步骤 + """ + # TODO: bcs cc服务下掉后,删除对应的接口 + bcs_cc_client = paas_cc.PaaSCCClient(comp_base.ComponentAuth(access_token)) + project_data = bcs_cc_client.update_project(project_id, data) + + updated_project_config = project.UpdatedProjectConfig( + projectID=project_id, + updater=data["updator"], + name=project_data["project_name"], + kind=ProjectKindName, + businessID=project_data["cc_app_id"], + ) + # 更新项目信息 + try: + resp = update_project(access_token, updated_project_config) + except Exception as e: + logger.error("update clustermanager project failed, %s", e) + return project_data + # 根据code判断项目是否存在,当项目不存在时,调用创建项目接口 + if resp.get("code") == constants.RECORD_NOT_EXIST_CODE: + # 组装写入cluster manager的数据 + reserved_config = project.ProjectReservedConfig( + bgID=project_data["bg_id"], + bgName=project_data["bg_name"], + deptName=project_data["dept_name"], + deptID=project_data["dept_id"], + centerID=project_data["center_id"], + centerName=project_data["center_name"], + isSecret=project_data["is_secrecy"], + isOffline=project_data["is_offlined"], + useBKRes=project_data["use_bk"], + projectType=project_data["project_type"], + ) + basic_config = project.ProjectBasicConfig( + projectID=project_id, + name=project_data["project_name"], + englishName=project_data["english_name"], + kind="k8s", + businessID=project_data["cc_app_id"], + description=project_data["description"], + ) + project_config = project.ProjectConfig( + creator=project_data["creator"], basic_config=basic_config, reserved_config=reserved_config + ) + # 当提示项目不存在时,需要添加项目 + try: + create_project(access_token, project_config) + except Exception as e: + logger.error("update or create clustermanager project failed, %s", e) + + return project_data diff --git a/bcs-app/backend/tests/components/bcs_api/__init__.py b/bcs-app/backend/tests/components/bcs_api/__init__.py new file mode 100644 index 000000000..e69de29bb From 0083d59bb208486a625246b7421ac1f7cf9f2296 Mon Sep 17 00:00:00 2001 From: bellkeyang Date: Tue, 14 Sep 2021 20:38:29 +0800 Subject: [PATCH 06/10] feat: write data to cluste manager --- .../testing_utils/mocks/bcs_api/__init__.py | 879 +----------------- 1 file changed, 1 insertion(+), 878 deletions(-) diff --git a/bcs-app/backend/tests/testing_utils/mocks/bcs_api/__init__.py b/bcs-app/backend/tests/testing_utils/mocks/bcs_api/__init__.py index e3844d00b..b2562d0a5 100644 --- a/bcs-app/backend/tests/testing_utils/mocks/bcs_api/__init__.py +++ b/bcs-app/backend/tests/testing_utils/mocks/bcs_api/__init__.py @@ -12,9 +12,7 @@ an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ -from typing import Dict, List - -from backend.components import bcs_api +from typing import Dict from ..utils import mockable_function @@ -32,878 +30,3 @@ def query_cluster_id(self, env_name: str, project_id: str, cluster_id: str) -> s @mockable_function def get_cluster_credentials(self, env_name: str, bcs_cluster_id: str) -> Dict: return {'server_address_path': '/foo', 'user_token': 'foo-token'} - - def create_project(self, *args, **kwargs) -> Dict: - return {"code": 0, "data": self.make_project_data()} - - def query_project(self, project_id) -> Dict: - return self.make_project_data() - - def update_project(self, *args, **kwargs) -> Dict: - return {"code": 0, "data": self.make_project_data()} - - def update_project_not_exist(self, *args, **kwargs) -> Dict: - return {"code": bcs_api.record_not_exist_code} - - def add_cluster(self, *args, **kwargs) -> Dict: - return {"code": 0, "data": self.make_add_cluster_resp()} - - def update_cluster(self, cluster_config: bcs_api.UpdatedClusterConfig) -> Dict: - return self.make_update_cluster_data() - - def delete_cluster( - self, cluster_id: str, is_force: bool = False, is_clean_resource=True, only_delete_info=False - ) -> Dict: - return {"code": 0, "data": self.make_delete_cluster_resp(cluster_id)} - - def add_nodes(self, cluster_id: str, node_config: bcs_api.NodeConfig) -> Dict: - return self.make_add_nodes_data() - - def delete_nodes(self, cluster_id: str, nodes: List[str], delete_mode: str, is_force: bool = False) -> Dict: - return self.make_delete_nodes_data() - - def query_task(self, task_id: str) -> Dict: - return self.make_query_task_data(task_id) - - @staticmethod - def make_project_data() -> Dict: - return { - "projectID": "string", - "name": "string", - "englishName": "string", - "creator": "string", - "updater": "string", - "projectType": 0, - "useBKRes": True, - "description": "string", - "isOffline": True, - "kind": "string", - "businessID": "string", - "deployType": 0, - "bgID": "string", - "bgName": "string", - "deptID": "string", - "deptName": "string", - "centerID": "string", - "centerName": "string", - "isSecret": True, - "credentials": { - "additionalProp1": {"key": "string", "secret": "string"}, - "additionalProp2": {"key": "string", "secret": "string"}, - "additionalProp3": {"key": "string", "secret": "string"}, - }, - "createTime": "string", - "updateTime": "string", - } - - @staticmethod - def make_add_cluster_resp() -> Dict: - return { - "code": 0, - "message": "string", - "result": True, - "data": { - "clusterID": "string", - "clusterName": "string", - "federationClusterID": "string", - "provider": "string", - "region": "string", - "vpcID": "string", - "projectID": "string", - "businessID": "string", - "environment": "string", - "engineType": "string", - "isExclusive": True, - "clusterType": "string", - "labels": {"additionalProp1": "string", "additionalProp2": "string", "additionalProp3": "string"}, - "creator": "string", - "createTime": "string", - "updateTime": "string", - "bcsAddons": { - "additionalProp1": { - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - "additionalProp2": { - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - "additionalProp3": { - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - }, - "extraAddons": { - "additionalProp1": { - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - "additionalProp2": { - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - "additionalProp3": { - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - }, - "systemID": "string", - "manageType": "string", - "master": { - "additionalProp1": { - "nodeID": "string", - "innerIP": "string", - "instanceType": "string", - "CPU": 0, - "mem": 0, - "GPU": 0, - "status": "string", - "zoneID": "string", - "nodeGroupID": "string", - "clusterID": "string", - }, - "additionalProp2": { - "nodeID": "string", - "innerIP": "string", - "instanceType": "string", - "CPU": 0, - "mem": 0, - "GPU": 0, - "status": "string", - "zoneID": "string", - "nodeGroupID": "string", - "clusterID": "string", - }, - "additionalProp3": { - "nodeID": "string", - "innerIP": "string", - "instanceType": "string", - "CPU": 0, - "mem": 0, - "GPU": 0, - "status": "string", - "zoneID": "string", - "nodeGroupID": "string", - "clusterID": "string", - }, - }, - "networkSettings": { - "clusterIPv4CIDR": "string", - "serviceIPv4CIDR": "string", - "maxNodePodNum": "string", - "maxServiceNum": "string", - }, - "clusterBasicSettings": { - "OS": "string", - "version": "string", - "clusterTags": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - "clusterAdvanceSettings": { - "IPVS": True, - "containerRuntime": "string", - "runtimeVersion": "string", - "extraArgs": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - "nodeSettings": {"dockerGraphPath": "string", "mountTarget": "string"}, - "status": "string", - "updator": "string", - }, - "task": { - "taskID": "string", - "taskType": "string", - "status": "string", - "message": "string", - "start": "string", - "end": "string", - "executionTime": 0, - "currentStep": "string", - "stepSequence": ["string"], - "steps": { - "additionalProp1": { - "name": "string", - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - "retry": 0, - "start": "string", - "end": "string", - "executionTime": 0, - "status": "string", - "message": "string", - "lastUpdate": "string", - }, - "additionalProp2": { - "name": "string", - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - "retry": 0, - "start": "string", - "end": "string", - "executionTime": 0, - "status": "string", - "message": "string", - "lastUpdate": "string", - }, - "additionalProp3": { - "name": "string", - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - "retry": 0, - "start": "string", - "end": "string", - "executionTime": 0, - "status": "string", - "message": "string", - "lastUpdate": "string", - }, - }, - "clusterID": "string", - "projectID": "string", - "creator": "string", - "lastUpdate": "string", - "updator": "string", - "forceTerminate": True, - }, - } - - @staticmethod - def make_update_cluster_data() -> Dict: - { - "clusterID": "string", - "clusterName": "string", - "federationClusterID": "string", - "provider": "string", - "region": "string", - "vpcID": "string", - "projectID": "string", - "businessID": "string", - "environment": "string", - "engineType": "string", - "isExclusive": True, - "clusterType": "string", - "labels": {"additionalProp1": "string", "additionalProp2": "string", "additionalProp3": "string"}, - "creator": "string", - "createTime": "string", - "updateTime": "string", - "bcsAddons": { - "additionalProp1": { - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - "additionalProp2": { - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - "additionalProp3": { - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - }, - "extraAddons": { - "additionalProp1": { - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - "additionalProp2": { - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - "additionalProp3": { - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - }, - "systemID": "string", - "manageType": "string", - "master": { - "additionalProp1": { - "nodeID": "string", - "innerIP": "string", - "instanceType": "string", - "CPU": 0, - "mem": 0, - "GPU": 0, - "status": "string", - "zoneID": "string", - "nodeGroupID": "string", - "clusterID": "string", - }, - "additionalProp2": { - "nodeID": "string", - "innerIP": "string", - "instanceType": "string", - "CPU": 0, - "mem": 0, - "GPU": 0, - "status": "string", - "zoneID": "string", - "nodeGroupID": "string", - "clusterID": "string", - }, - "additionalProp3": { - "nodeID": "string", - "innerIP": "string", - "instanceType": "string", - "CPU": 0, - "mem": 0, - "GPU": 0, - "status": "string", - "zoneID": "string", - "nodeGroupID": "string", - "clusterID": "string", - }, - }, - "networkSettings": { - "clusterIPv4CIDR": "string", - "serviceIPv4CIDR": "string", - "maxNodePodNum": "string", - "maxServiceNum": "string", - }, - "clusterBasicSettings": { - "OS": "string", - "version": "string", - "clusterTags": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - "clusterAdvanceSettings": { - "IPVS": True, - "containerRuntime": "string", - "runtimeVersion": "string", - "extraArgs": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - "nodeSettings": {"dockerGraphPath": "string", "mountTarget": "string"}, - "status": "string", - "updator": "string", - } - - @staticmethod - def make_delete_cluster_resp(cluster_id: str) -> Dict: - return { - "code": 0, - "message": "string", - "result": True, - "data": { - "clusterID": cluster_id, - "clusterName": "string", - "federationClusterID": "string", - "provider": "string", - "region": "string", - "vpcID": "string", - "projectID": "string", - "businessID": "string", - "environment": "string", - "engineType": "string", - "isExclusive": True, - "clusterType": "string", - "labels": {"additionalProp1": "string", "additionalProp2": "string", "additionalProp3": "string"}, - "creator": "string", - "createTime": "string", - "updateTime": "string", - "bcsAddons": { - "additionalProp1": { - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - "additionalProp2": { - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - "additionalProp3": { - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - }, - "extraAddons": { - "additionalProp1": { - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - "additionalProp2": { - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - "additionalProp3": { - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - }, - "systemID": "string", - "manageType": "string", - "master": { - "additionalProp1": { - "nodeID": "string", - "innerIP": "string", - "instanceType": "string", - "CPU": 0, - "mem": 0, - "GPU": 0, - "status": "string", - "zoneID": "string", - "nodeGroupID": "string", - "clusterID": "string", - }, - "additionalProp2": { - "nodeID": "string", - "innerIP": "string", - "instanceType": "string", - "CPU": 0, - "mem": 0, - "GPU": 0, - "status": "string", - "zoneID": "string", - "nodeGroupID": "string", - "clusterID": "string", - }, - "additionalProp3": { - "nodeID": "string", - "innerIP": "string", - "instanceType": "string", - "CPU": 0, - "mem": 0, - "GPU": 0, - "status": "string", - "zoneID": "string", - "nodeGroupID": "string", - "clusterID": "string", - }, - }, - "networkSettings": { - "clusterIPv4CIDR": "string", - "serviceIPv4CIDR": "string", - "maxNodePodNum": "string", - "maxServiceNum": "string", - }, - "clusterBasicSettings": { - "OS": "string", - "version": "string", - "clusterTags": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - "clusterAdvanceSettings": { - "IPVS": True, - "containerRuntime": "string", - "runtimeVersion": "string", - "extraArgs": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - }, - "nodeSettings": {"dockerGraphPath": "string", "mountTarget": "string"}, - "status": "string", - "updator": "string", - }, - "tasks": [ - { - "taskID": "string", - "taskType": "string", - "status": "string", - "message": "string", - "start": "string", - "end": "string", - "executionTime": 0, - "currentStep": "string", - "stepSequence": ["string"], - "steps": { - "additionalProp1": { - "name": "string", - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - "retry": 0, - "start": "string", - "end": "string", - "executionTime": 0, - "status": "string", - "message": "string", - "lastUpdate": "string", - }, - "additionalProp2": { - "name": "string", - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - "retry": 0, - "start": "string", - "end": "string", - "executionTime": 0, - "status": "string", - "message": "string", - "lastUpdate": "string", - }, - "additionalProp3": { - "name": "string", - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - "retry": 0, - "start": "string", - "end": "string", - "executionTime": 0, - "status": "string", - "message": "string", - "lastUpdate": "string", - }, - }, - "clusterID": "string", - "projectID": "string", - "creator": "string", - "lastUpdate": "string", - "updator": "string", - "forceTerminate": True, - } - ], - } - - @staticmethod - def make_add_nodes_data() -> Dict: - return { - "taskID": "string", - "taskType": "string", - "status": "string", - "message": "string", - "start": "string", - "end": "string", - "executionTime": 0, - "currentStep": "string", - "stepSequence": ["string"], - "steps": { - "additionalProp1": { - "name": "string", - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - "retry": 0, - "start": "string", - "end": "string", - "executionTime": 0, - "status": "string", - "message": "string", - "lastUpdate": "string", - }, - "additionalProp2": { - "name": "string", - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - "retry": 0, - "start": "string", - "end": "string", - "executionTime": 0, - "status": "string", - "message": "string", - "lastUpdate": "string", - }, - "additionalProp3": { - "name": "string", - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - "retry": 0, - "start": "string", - "end": "string", - "executionTime": 0, - "status": "string", - "message": "string", - "lastUpdate": "string", - }, - }, - "clusterID": "string", - "projectID": "string", - "creator": "string", - "lastUpdate": "string", - "updator": "string", - "forceTerminate": True, - } - - @staticmethod - def make_delete_nodes_data() -> Dict: - return { - "taskID": "string", - "taskType": "string", - "status": "string", - "message": "string", - "start": "string", - "end": "string", - "executionTime": 0, - "currentStep": "string", - "stepSequence": ["string"], - "steps": { - "additionalProp1": { - "name": "string", - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - "retry": 0, - "start": "string", - "end": "string", - "executionTime": 0, - "status": "string", - "message": "string", - "lastUpdate": "string", - }, - "additionalProp2": { - "name": "string", - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - "retry": 0, - "start": "string", - "end": "string", - "executionTime": 0, - "status": "string", - "message": "string", - "lastUpdate": "string", - }, - "additionalProp3": { - "name": "string", - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - "retry": 0, - "start": "string", - "end": "string", - "executionTime": 0, - "status": "string", - "message": "string", - "lastUpdate": "string", - }, - }, - "clusterID": "string", - "projectID": "string", - "creator": "string", - "lastUpdate": "string", - "updator": "string", - "forceTerminate": True, - } - - @staticmethod - def make_query_task_data(task_id: str) -> Dict: - return { - "taskID": task_id, - "taskType": "string", - "status": "string", - "message": "string", - "start": "string", - "end": "string", - "executionTime": 0, - "currentStep": "string", - "stepSequence": ["string"], - "steps": { - "additionalProp1": { - "name": "string", - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - "retry": 0, - "start": "string", - "end": "string", - "executionTime": 0, - "status": "string", - "message": "string", - "lastUpdate": "string", - }, - "additionalProp2": { - "name": "string", - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - "retry": 0, - "start": "string", - "end": "string", - "executionTime": 0, - "status": "string", - "message": "string", - "lastUpdate": "string", - }, - "additionalProp3": { - "name": "string", - "system": "string", - "link": "string", - "params": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string", - }, - "retry": 0, - "start": "string", - "end": "string", - "executionTime": 0, - "status": "string", - "message": "string", - "lastUpdate": "string", - }, - }, - "clusterID": "string", - "projectID": "string", - "creator": "string", - "lastUpdate": "string", - "updator": "string", - "forceTerminate": True, - } From 1ecbbd07ba7df4fb99de8d4196e53095325797cd Mon Sep 17 00:00:00 2001 From: bellkeyang Date: Tue, 14 Sep 2021 22:10:28 +0800 Subject: [PATCH 07/10] feat: write date to cluster manager --- .../backend/tests/components/test_bcs_api.py | 135 ------------------ 1 file changed, 135 deletions(-) diff --git a/bcs-app/backend/tests/components/test_bcs_api.py b/bcs-app/backend/tests/components/test_bcs_api.py index 72a2bdb3b..47f5ee98d 100644 --- a/bcs-app/backend/tests/components/test_bcs_api.py +++ b/bcs-app/backend/tests/components/test_bcs_api.py @@ -19,9 +19,6 @@ BCS_AUTH_TOKEN = 'example-auth-token' -fake_ip = "127.0.0.1" -success_code = 0 - @pytest.fixture(autouse=True) def setup_token(settings): @@ -47,135 +44,3 @@ def test_get_cluster_credentials(self, requests_mock): client = BcsApiClient(ComponentAuth('fake_token')) resp = client.get_cluster_credentials('stag', 'fake-bcs-cluster-foo') assert resp == {'name': 'foo'} - - def test_query_project(self, project_id, request_user, requests_mock): - expected_data = {"project_id": project_id} - requests_mock.get(ANY, json={"code": success_code, "data": expected_data}) - - client = BcsApiClient(ComponentAuth(request_user.token.access_token)) - resp = client.query_project(project_id) - assert resp == expected_data - - def test_create_project(self, project_id, request_user, random_name, requests_mock): - requests_mock.post(ANY, json={"code": success_code, "message": "success"}) - - client = BcsApiClient(ComponentAuth(request_user.token.access_token)) - basic_config = bcs_api.ProjectBasicConfig( - projectID=project_id, - name=random_name, - englishName=random_name, - kind="k8s", - businessID=1, - description="", - credentials={}, - ) - reserved_config = bcs_api.ProjectReservedConfig( - bgID="", - bgName="", - deptID="", - deptName="", - centerID="", - centerName="", - isSecret=False, - deployType=2, - isOffline=False, - useBKRes=False, - projectType=1, - ) - project_config = bcs_api.ProjectConfig( - creator=request_user.username, basic_config=basic_config, reserved_config=reserved_config - ) - resp = client.create_project(project_config) - assert resp["code"] == success_code - - def test_update_project(self, project_id, request_user, random_name, requests_mock): - expected_data = {"project_id": project_id} - requests_mock.put(ANY, json={"code": success_code, "data": expected_data}) - - client = BcsApiClient(ComponentAuth(request_user.token.access_token)) - project_config = bcs_api.UpdatedProjectConfig( - projectID=project_id, - updater=request_user.username, - name=random_name, - kind="k8s", - businessID=1, - ) - resp = client.update_project(project_config) - assert resp["code"] == success_code - - def test_add_cluster(self, project_id, cluster_id, request_user, random_name, requests_mock): - expected_data = {"cluster_id": cluster_id} - expected_task = {"taskID": random_name} - requests_mock.post(ANY, json={"code": 0, "data": expected_data, "task": expected_task}) - - client = BcsApiClient(ComponentAuth(request_user.token.access_token)) - cloud_cluster_config = bcs_api.CloudClusterConfig( - region=random_name, - manageType="INDEPENDENT_CLUSTER", - master=["fake_ip"], - vpcID=random_name, - cloudID=1, - nodes=[], - networkSettings={}, - clusterBasicSettings={}, - clusterAdvanceSettings={}, - nodeSettings={}, - systemReinstall=False, - initLoginPassword="", - status="", - ) - bcs_cluster_config = bcs_api.BcsClusterConfig( - projectID=project_id, - businessID=1, - clusterID=cluster_id, - clusterName=random_name, - provider="", - environment="test", - engineType="k8s", - isExclusive=False, - clusterType="k8s", - federationClusterID="", - labels={}, - onlyCreateInfo=True, - bcsAddons={}, - extraAddons={}, - ) - cluster_config = bcs_api.ClusterConfig( - creator=request_user.username, - cloud_cluster_config=cloud_cluster_config, - bcs_cluster_config=bcs_cluster_config, - ) - resp = client.add_cluster(cluster_config) - assert resp["task"] == expected_task - assert resp["data"] == expected_data - - def test_update_cluster(self, project_id, cluster_id, request_user, random_name, requests_mock): - expected_data = {"cluster_id": cluster_id} - requests_mock.put(ANY, json={"code": 0, "data": expected_data}) - - client = BcsApiClient(ComponentAuth(request_user.token.access_token)) - cluster_config = bcs_api.UpdatedClusterConfig( - projectID=project_id, - clusterID=cluster_id, - updater=request_user.username, - clusterName="test", - status="RUNNING", - ) - resp = client.update_cluster(cluster_config) - assert resp == expected_data - - def test_delete_cluster(self, cluster_id, random_name, request_user, requests_mock): - expected_task = {"taskID": random_name} - requests_mock.delete(ANY, json={"code": 0, "data": {}, "task": expected_task}) - - client = BcsApiClient(ComponentAuth(request_user.token.access_token)) - resp = client.delete_cluster(cluster_id) - assert resp["task"] == expected_task - - def test_query_task(self, random_name, request_user, requests_mock): - expected_data = {"taskID": random_name} - requests_mock.get(ANY, json={"code": 0, "data": expected_data}) - - client = BcsApiClient(ComponentAuth(request_user.token.access_token)) - resp = client.query_task(random_name) - assert resp["data"] == expected_data From 67c0faaa88ac5e650ea43cc5e735e4a1e6be906f Mon Sep 17 00:00:00 2001 From: bellkeyang Date: Wed, 15 Sep 2021 11:54:34 +0800 Subject: [PATCH 08/10] feat: write data to cluster manager --- bcs-app/backend/tests/testing_utils/mocks/bcs_api/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bcs-app/backend/tests/testing_utils/mocks/bcs_api/project.py b/bcs-app/backend/tests/testing_utils/mocks/bcs_api/project.py index 477e35214..6cd8a9ae6 100644 --- a/bcs-app/backend/tests/testing_utils/mocks/bcs_api/project.py +++ b/bcs-app/backend/tests/testing_utils/mocks/bcs_api/project.py @@ -46,7 +46,7 @@ def make_project_data() -> Dict: "projectType": 0, "useBKRes": True, "description": "string", - "isOffline": True, + "isOffline": False, "kind": "string", "businessID": "string", "deployType": 0, From 7721bee97186a7f2f14a8ef32e591299d08e7881 Mon Sep 17 00:00:00 2001 From: bellkeyang Date: Thu, 23 Sep 2021 15:10:24 +0800 Subject: [PATCH 09/10] feat: write date to cluster manager --- bcs-app/backend/components/bcs_api/__init__.py | 12 ++++++++++++ bcs-app/backend/components/bcs_api/cluster.py | 4 ++-- bcs-app/backend/components/bcs_api/project.py | 4 ++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/bcs-app/backend/components/bcs_api/__init__.py b/bcs-app/backend/components/bcs_api/__init__.py index d2c320661..bc26fa487 100644 --- a/bcs-app/backend/components/bcs_api/__init__.py +++ b/bcs-app/backend/components/bcs_api/__init__.py @@ -12,6 +12,7 @@ an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ +import json from typing import Dict from django.conf import settings @@ -49,6 +50,17 @@ def __call__(self, r: PreparedRequest): return r +class BcsApiGatewayAuth(BcsApiAuth): + """用于调用 bcs-api-gateway 系统的鉴权对象""" + + def __call__(self, r: PreparedRequest): + # 从配置文件读取访问系统的 admin token,置入请求头中 + r.headers["Authorization"] = f"Bearer {getattr(settings, 'BCS_API_GW_AUTH_TOKEN', '')}" + r.headers["Content-Type"] = "application/json" + r.headers['X-BKAPI-AUTHORIZATION'] = json.dumps({"access_token": self.access_token}) + return r + + class BcsApiClient(BkApiClient): """访问 BCS API 服务的 Client 对象 diff --git a/bcs-app/backend/components/bcs_api/cluster.py b/bcs-app/backend/components/bcs_api/cluster.py index 66a3b4317..30da92c5e 100644 --- a/bcs-app/backend/components/bcs_api/cluster.py +++ b/bcs-app/backend/components/bcs_api/cluster.py @@ -18,7 +18,7 @@ from django.conf import settings from ..base import BaseHttpClient, BkApiClient, ComponentAuth, response_handler -from . import BcsApiAuth +from . import BcsApiGatewayAuth from .constants import BCS_API_VERSION, ClusterManageType, ManageType @@ -125,7 +125,7 @@ class BcsClusterApiClient(BkApiClient): def __init__(self, auth: ComponentAuth): self._config = BcsApiGatewayConfig(host=settings.BCS_API_GW_DOMAIN, env=settings.BCS_API_GW_ENV) - self._client = BaseHttpClient(BcsApiAuth(auth.access_token)) + self._client = BaseHttpClient(BcsApiGatewayAuth(auth.access_token)) def add_cluster(self, cluster_config: ClusterConfig) -> Dict: """添加集群 diff --git a/bcs-app/backend/components/bcs_api/project.py b/bcs-app/backend/components/bcs_api/project.py index f2bd791e2..a8746f7ba 100644 --- a/bcs-app/backend/components/bcs_api/project.py +++ b/bcs-app/backend/components/bcs_api/project.py @@ -18,7 +18,7 @@ from django.conf import settings from ..base import BaseHttpClient, BkApiClient, ComponentAuth, response_handler -from . import BcsApiAuth +from . import BcsApiGatewayAuth from .constants import BCS_API_VERSION, DeployType, ProjectType @@ -96,7 +96,7 @@ class BcsProjectApiClient(BkApiClient): def __init__(self, auth: ComponentAuth): self._config = BcsApiGatewayConfig(host=settings.BCS_API_GW_DOMAIN, env=settings.BCS_API_GW_ENV) - self._client = BaseHttpClient(BcsApiAuth(auth.access_token)) + self._client = BaseHttpClient(BcsApiGatewayAuth(auth.access_token)) @response_handler() def query_project(self, project_id: str) -> Dict: From 84b7589f72b3a9e9e54863c5e10dc0317ce3bb46 Mon Sep 17 00:00:00 2001 From: bellkeyang Date: Thu, 23 Sep 2021 15:19:39 +0800 Subject: [PATCH 10/10] feat: write data to cluste manager --- bcs-app/backend/components/bcs_api/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bcs-app/backend/components/bcs_api/__init__.py b/bcs-app/backend/components/bcs_api/__init__.py index bc26fa487..8150da549 100644 --- a/bcs-app/backend/components/bcs_api/__init__.py +++ b/bcs-app/backend/components/bcs_api/__init__.py @@ -54,7 +54,7 @@ class BcsApiGatewayAuth(BcsApiAuth): """用于调用 bcs-api-gateway 系统的鉴权对象""" def __call__(self, r: PreparedRequest): - # 从配置文件读取访问系统的 admin token,置入请求头中 + # 从配置文件读取访问系统的 admin token, 放置到请求头中 r.headers["Authorization"] = f"Bearer {getattr(settings, 'BCS_API_GW_AUTH_TOKEN', '')}" r.headers["Content-Type"] = "application/json" r.headers['X-BKAPI-AUTHORIZATION'] = json.dumps({"access_token": self.access_token})