Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: write data to cluster manager #1461

Closed
wants to merge 12 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
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
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:
Expand Down Expand Up @@ -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 对象

Expand All @@ -69,7 +81,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)
Expand All @@ -81,7 +92,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)
Expand Down
206 changes: 206 additions & 0 deletions bcs-app/backend/components/bcs_api/cluster.py
Original file line number Diff line number Diff line change
@@ -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 BcsApiGatewayAuth
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(BcsApiGatewayAuth(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)
53 changes: 53 additions & 0 deletions bcs-app/backend/components/bcs_api/constants.py
Original file line number Diff line number Diff line change
@@ -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=_("联邦集群"))
Loading