Exemplo n.º 1
0
class SopsClient(BkApiClient):
    def __init__(self):
        self._config = SopsConfig(host=settings.SOPS_API_HOST)
        self._client = BaseHttpClient(SopsAuth())

    @response_handler(default=dict)
    def create_task(self, bk_biz_id: str, template_id: str,
                    data: CreateTaskParams) -> Dict:
        """通过业务流程创建任务

        :param bk_biz_id: 作业模板所属的业务ID
        :param template_id: 作业模板ID
        :param data: 创建任务实例需要的参数,包含名称、申请人、业务等
        :returns: 返回任务详情,包含任务连接、步骤名称、任务ID
        """
        url = self._config.create_task_url.format(template_id=template_id,
                                                  bk_biz_id=bk_biz_id)
        return self._client.request_json("POST", url, json=asdict(data))

    @response_handler(default=dict)
    def start_task(self, bk_biz_id: str, task_id: str) -> Dict:
        """启动任务

        :param bk_biz_id: 作业模板所属的业务ID
        :param task_id: 任务ID
        :returns: 返回的数据中,包含任务连接
        """
        url = self._config.start_task_url.format(task_id=task_id,
                                                 bk_biz_id=bk_biz_id)
        return self._client.request_json("POST", url)

    @response_handler(default=dict)
    def get_task_status(self, bk_biz_id: str, task_id: str) -> Dict:
        """获取任务状态

        :param bk_biz_id: 作业模板所属的业务ID
        :param task_id: 任务ID
        :returns: 返回任务执行状态,包含启动时间、任务状态、子步骤名称、子步骤状态等
        """
        url = self._config.get_task_status_url.format(task_id=task_id,
                                                      bk_biz_id=bk_biz_id)
        return self._client.request_json("GET", url)

    @response_handler(default=dict)
    def get_task_node_data(self, bk_biz_id: str, task_id: str,
                           node_id: str) -> Dict:
        """获取任务步骤的详情

        :param bk_biz_id: 作业模板所属的业务ID
        :param task_id: 任务ID
        :param node_id: 子步骤的ID
        :returns: 返回子步骤输出,便于解析输出,从而得到对应的value
        """
        url = self._config.get_task_node_data_url.format(task_id=task_id,
                                                         bk_biz_id=bk_biz_id)
        return self._client.request_json("GET",
                                         url,
                                         params={"node_id": node_id})
Exemplo n.º 2
0
class PaaSCCClient(BkApiClient):
    """访问 PaaSCC 服务的 Client 对象

    :param auth: 包含校验信息的对象
    """

    def __init__(self, auth: ComponentAuth):
        self._config = PaaSCCConfig(host=BCS_CC_API_PRE_URL)
        self._client = BaseHttpClient(auth.to_header_api_auth())

    def get_cluster(self, project_id: str, cluster_id: str) -> Dict:
        """获取集群信息"""
        url = self._config.get_cluster_url.format(project_id=project_id, cluster_id=cluster_id)
        return self._client.request_json('GET', url)

    @parse_response_data()
    def get_project(self, project_id: str) -> Dict:
        """获取项目信息"""
        url = self._config.get_project_url.format(project_id=project_id)
        return self._client.request_json('GET', url)

    @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,名称、描述等
        """
        url = self._config.update_cluster_url.format(project_id=project_id, cluster_id=cluster_id)
        return self._client.request_json("PUT", url, json=data)

    @response_handler()
    def delete_cluster(self, project_id: str, cluster_id: str):
        """删除集群

        :param project_id: 项目32为长度 ID
        :param cluster_id: 集群ID
        """
        url = self._config.delete_cluster_url.format(project_id=project_id, cluster_id=cluster_id)
        return self._client.request_json("DELETE", url)

    @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和状态
        :returns: 返回更新的节点信息
        """
        url = self._config.update_node_list_url.format(project_id=project_id, cluster_id=cluster_id)
        return self._client.request_json("PATCH", url, json={"updates": [asdict(node) for node in nodes]})
Exemplo n.º 3
0
class PaaSCCClient(BkApiClient):
    """访问 PaaSCC 服务的 Client 对象

    :param auth: 包含校验信息的对象
    """

    def __init__(self, auth: ComponentAuth):
        self._config = PaaSCCConfig(host=BCS_CC_API_PRE_URL)
        self._client = BaseHttpClient(auth.to_header_api_auth())

    def get_cluster(self, project_id: str, cluster_id: str) -> Dict:
        """获取集群信息"""
        url = self._config.get_cluster_url.format(project_id=project_id, cluster_id=cluster_id)
        return self._client.request_json('GET', url)

    @parse_response_data()
    def get_project(self, project_id: str) -> Dict:
        """获取项目信息"""
        url = self._config.get_project_url.format(project_id=project_id)
        return self._client.request_json('GET', url)
Exemplo n.º 4
0
class ProxyClient(BkApiClient):
    """访问代理服务的client

    :param proxy_data: 访问代理需要的内容,包含host、token等
    """
    def __init__(self, proxy_config: Optional[ProxyConfig] = None):
        self.proxy_config = proxy_config
        self.request = proxy_config.request
        self._client = BaseHttpClient(
            ProxyAuth(proxy_config.token, proxy_config.content_type))

    @property
    def source_url(self):
        """获取真实服务的url
        去除proxy配置中添加的前缀,获取真正的路径,再添加域名组装到真正路径
        """
        source_path = self.request.path
        if self.proxy_config:
            source_path = self.request.path.split(
                self.proxy_config.prefix_path)[-1]
        return urljoin(self.proxy_config.host, source_path)

    @property
    def request_params(self):
        """获取请求的params数据
        TODO: 后续可能会有要删除的字段
        """
        return self.request.query_params

    @property
    def request_data(self):
        """获取请求的data数据"""
        return self.request.data

    def proxy(self, **kwargs):
        return self._client.request_json(
            self.request.method,
            self.source_url,
            params=self.request_params,
            json=self.request_data,
            verify=self.proxy_config.verify_ssl,
            **kwargs,
        )
Exemplo n.º 5
0
class BkRepoClient(BkApiClient):
    """访问注册到apigw的 Api"""

    PROJECT_EXIST_CODE = 251005  # 项目已经存在
    REPO_EXIST_CODE = 251007  # 仓库已经存在

    def __init__(self,
                 username: str,
                 access_token: str = None,
                 password: str = None):
        self._config = BkRepoApigwConfig()
        self._bk_repo_raw_config = BkRepoRawConfig()
        self._client = BaseHttpClient(
            BkRepoAuth(access_token, username, password), )

    def create_project(self, project_code: str, project_name: str,
                       description: str) -> Dict:
        """创建仓库所属项目

        :param project_code: BCS项目code
        :param project_name: BCS项目名称
        :param description: BCS项目描述
        :returns: 返回项目
        """
        data = {
            "name": project_code,
            "displayName": project_name,
            "description": description
        }
        resp = self._client.request_json("POST",
                                         self._config.create_project,
                                         json=data,
                                         raise_for_status=False)
        if resp.get("code") not in [
                ErrorCode.NoError, self.PROJECT_EXIST_CODE
        ]:
            raise BkRepoCreateProjectError(
                f"create project error, {resp.get('message')}")
        return resp

    def create_chart_repo(self, project_code: str) -> Dict:
        """创建chart 仓库

        :param project_code: BCS项目code
        :returns: 返回仓库
        """
        data = {
            "projectId": project_code,
            "name": project_code,
            "type": "HELM",
            "category": "LOCAL",
            "public": False,  # 容器服务项目自己的仓库
            "configuration": {
                "type": "local"
            },
        }
        resp = self._client.request_json("POST",
                                         self._config.create_chart_repo,
                                         json=data,
                                         raise_for_status=False)
        if resp.get("code") not in [ErrorCode.NoError, self.REPO_EXIST_CODE]:
            raise BkRepoCreateRepoError(
                f"create repo error, {resp.get('message')}")
        return resp

    @response_handler()
    def set_auth(self, project_code: str, repo_admin_user: str,
                 repo_admin_pwd: str) -> bool:
        """设置权限

        :param project_code: BCS项目code
        :param repo_admin_user: 仓库admin用户
        :param repo_admin_pwd: 仓库admin密码
        :returns: 返回auth信息
        """
        data = {
            "admin": False,
            "name": repo_admin_user,
            "pwd": repo_admin_pwd,
            "userId": repo_admin_user,
            "asstUsers": [repo_admin_user],
            "group": True,
            "projectId": project_code,
        }
        return self._client.request_json("POST",
                                         self._config.set_user_auth,
                                         json=data,
                                         raise_for_status=False)

    def list_charts(self,
                    project_name: str,
                    repo_name: str,
                    start_time: str = None) -> Dict:
        """获取项目下的chart

        :param project_name: 项目名称
        :param repo_name: 仓库名称
        :param start_time: 增量查询的起始时间
        :returns: 返回项目下的chart列表
        """
        url = self._bk_repo_raw_config.list_charts.format(
            project_name=project_name, repo_name=repo_name)
        return self._client.request_json("GET",
                                         url,
                                         params={"startTime": start_time})

    def get_chart_versions(self, project_name: str, repo_name: str,
                           chart_name: str) -> List:
        """获取项目下指定chart的版本列表

        :param project_name: 项目名称
        :param repo_name: 仓库名称
        :param chart_name: chart 名称
        :returns: 返回chart版本列表
        """
        url = self._bk_repo_raw_config.get_chart_versions.format(
            project_name=project_name,
            repo_name=repo_name,
            chart_name=chart_name)
        return self._client.request_json("GET", url)

    def get_chart_version_detail(self, project_name: str, repo_name: str,
                                 chart_name: str, version: str) -> Dict:
        """获取指定chart版本的详情

        :param project_name: 项目名称
        :param repo_name: 仓库名称
        :param chart_name: chart 名称
        :param version: chart 版本
        :returns: 返回chart版本详情,包含名称、创建时间、版本、url等
        """
        url = self._bk_repo_raw_config.get_chart_version_detail.format(
            project_name=project_name,
            repo_name=repo_name,
            chart_name=chart_name,
            version=version)
        return self._client.request_json("GET", url)

    def delete_chart_version(self, project_name: str, repo_name: str,
                             chart_name: str, version: str) -> Dict:
        """删除chart版本

        :param project_name: 项目名称
        :param repo_name: 仓库名称
        :param chart_name: chart 名称
        :param version: chart 版本
        :returns: 返回删除信息,格式: {"deleted": True}
        """
        url = self._bk_repo_raw_config.delete_chart_version.format(
            project_name=project_name,
            repo_name=repo_name,
            chart_name=chart_name,
            version=version)
        resp = self._client.request_json("DELETE", url)
        if not (resp.get("deleted")
                or "no such file or directory" in resp.get("error", "")):
            raise BkRepoDeleteVersionError(
                f"delete chart version error, {resp}")
        return resp
Exemplo n.º 6
0
class BkCCClient(BkApiClient):
    """CMDB API SDK"""
    def __init__(self,
                 username: str,
                 bk_supplier_account: Optional[str] = settings.
                 BKCC_DEFAULT_SUPPLIER_ACCOUNT):
        self._config = BkCCConfig(host=settings.COMPONENT_HOST)
        self._client = BaseHttpClient(
            BkCCAuth(username, bk_supplier_account=bk_supplier_account))

    @response_handler(default=dict)
    def search_business(
        self,
        page: PageData,
        fields: Optional[List] = None,
        condition: Optional[Dict] = None,
        bk_supplier_account: Optional[str] = None,
    ) -> Dict:
        """
        获取业务信息

        :param page: 分页条件
        :param fields: 返回的字段
        :param condition: 查询条件
        :parma bk_supplier_account: 供应商
        :return: 返回业务信息,格式:{'count': 1, 'info': [{'id': 1}]}
        """
        url = self._config.search_business_url
        params = {
            'page': asdict(page),
            'fields': fields,
            'condition': condition,
            'bk_supplier_account': bk_supplier_account,
        }
        return self._client.request_json("POST", url, json=params)

    @response_handler(default=list)
    def search_biz_inst_topo(self, bk_biz_id: int) -> List:
        """
        获取业务拓扑信息

        :param bk_biz_id: 业务 ID
        :return: 业务拓扑信息
        """
        url = self._config.search_biz_inst_topo_url
        params = {'bk_biz_id': bk_biz_id}
        return self._client.request_json('POST', url, json=params)

    @response_handler(default=dict)
    def get_biz_internal_module(self, bk_biz_id: int) -> Dict:
        """
        查询内部模块拓扑

        :param bk_biz_id: 业务 ID
        :return: 内部模块拓扑信息
        """
        url = self._config.get_biz_internal_module_url
        params = {'bk_biz_id': bk_biz_id}
        return self._client.request_json('POST', url, json=params)

    @response_handler(default=dict)
    def list_biz_hosts(
        self,
        bk_biz_id: int,
        page: PageData,
        bk_set_ids: List,
        bk_module_ids: List,
        fields: List,
        host_property_filter: Optional[Dict] = None,
        bk_supplier_account: str = None,
    ) -> Dict:
        """
        获取业务主机信息

        :param bk_biz_id: 业务 ID
        :param page: 分页配置
        :param bk_set_ids: 集群 ID 列表
        :param bk_module_ids: 模块 ID 列表
        :param fields: 指定的字段信息
        :param host_property_filter: 主机属性组合查询条件
        :param bk_supplier_account: 供应商
        :return: 主机信息,格式:{'count': 1, 'info': [{'bk_host_innerip': '127.0.0.1'}]}
        """
        url = self._config.list_biz_hosts_url
        params = {
            'bk_biz_id': bk_biz_id,
            'page': asdict(page),
            'bk_set_ids': bk_set_ids,
            'bk_module_ids': bk_module_ids,
            'fields': fields,
            'host_property_filter': host_property_filter,
            'bk_supplier_account': bk_supplier_account,
        }
        return self._client.request_json('POST', url, json=params)
Exemplo n.º 7
0
class PaaSCCClient(BkApiClient):
    """访问 PaaSCC 服务的 Client 对象

    :param auth: 包含校验信息的对象
    """
    def __init__(self, auth: ComponentAuth):
        self._config = PaaSCCConfig(host=BCS_CC_API_PRE_URL)
        self._client = BaseHttpClient(auth.to_header_api_auth())

    def get_cluster(self, project_id: str, cluster_id: str) -> Dict:
        """获取集群信息"""
        if cluster_id in [
                cluster["cluster_id"] for cluster in get_shared_clusters()
        ]:
            url = self._config.get_cluster_by_id_url.format(
                cluster_id=cluster_id)
            return self._client.request_json('GET', url)
        url = self._config.get_cluster_url.format(project_id=project_id,
                                                  cluster_id=cluster_id)
        return self._client.request_json('GET', url)

    @response_handler()
    def get_cluster_by_id(self, cluster_id: str) -> Dict:
        """根据集群ID获取集群信息"""
        url = self._config.get_cluster_by_id_url.format(cluster_id=cluster_id)
        return self._client.request_json('GET', url)

    @response_handler()
    def list_clusters(self, cluster_ids: List[str]) -> Dict:
        """根据集群ID列表批量获取集群信息"""
        url = self._config.list_clusters_url
        data = {"cluster_ids": cluster_ids}
        # ugly: search_project 的设置才能使接口生效
        return self._client.request_json('POST',
                                         url,
                                         params={'search_project': 1},
                                         json=data)

    @parse_response_data()
    def get_project(self, project_id: str) -> Dict:
        """获取项目信息"""
        url = self._config.get_project_url.format(project_id=project_id)
        return self._client.request_json('GET', url)

    @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,名称、描述等
        """
        url = self._config.update_cluster_url.format(project_id=project_id,
                                                     cluster_id=cluster_id)
        return self._client.request_json("PUT", url, json=data)

    @response_handler()
    def delete_cluster(self, project_id: str, cluster_id: str):
        """删除集群

        :param project_id: 项目32为长度 ID
        :param cluster_id: 集群ID
        """
        url = self._config.delete_cluster_url.format(project_id=project_id,
                                                     cluster_id=cluster_id)
        return self._client.request_json("DELETE", url)

    @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和状态
        :returns: 返回更新的节点信息
        """
        url = self._config.update_node_list_url.format(project_id=project_id,
                                                       cluster_id=cluster_id)
        return self._client.request_json(
            "PATCH", url, json={"updates": [asdict(node) for node in nodes]})

    @response_handler()
    def get_cluster_namespace_list(
        self,
        project_id: str,
        cluster_id: str,
        limit=None,
        offset=None,
        with_lb: Union[bool, int] = False,
        desire_all_data: Union[bool, int] = None,
    ) -> Dict[str, Union[int, List[Dict]]]:
        """获取集群下命名空间列表

        :param project_id: 项目ID
        :param cluster_id: 集群ID
        :param limit: 每页的数量
        :param offset: 第几页
        :param with_lb: 是否返回lb,兼容了bool和int型
        :param desire_all_data: 是否拉取集群下全量命名空间,兼容bool和int型
        :returns: 返回集群下的命名空间
        """
        url = self._config.get_cluster_namespace_list_url.format(
            project_id=project_id, cluster_id=cluster_id)
        req_params = {"desire_all_data": desire_all_data}
        # NOTE: 根据上层调用,希望获取的是集群下的所有命名空间,因此,当desire_all_data为None时,设置为拉取全量
        if desire_all_data is None:
            req_params["desire_all_data"] = 1
        if limit:
            req_params["limit"] = limit
        if offset:
            req_params["offset"] = offset
        if with_lb:
            req_params["with_lb"] = with_lb

        return self._client.request_json("GET", url, params=req_params)

    @response_handler()
    def get_node_list(self,
                      project_id: str,
                      cluster_id: str,
                      params: Dict = None) -> Dict:
        """获取节点列表
        :param project_id: 项目ID
        :param cluster_id: 集群ID
        :param params: 额外的参数
        :returns: 返回节点列表,格式: {"count": 1, "results": [node的详情信息]}
        """
        url = self._config.get_node_list_url.format(project_id=project_id)
        req_params = {"cluster_id": cluster_id}
        if params and isinstance(params, dict):
            req_params.update(params)
        # 默认拉取项目或集群下的所有节点,防止返回分页数据,导致数据不准确
        req_params.setdefault("desire_all_data", 1)
        return self._client.request_json("GET", url, params=req_params)

    @response_handler()
    def list_projects_by_ids(self, project_ids: List[str]) -> Dict:
        """获取项目列表
        :param project_ids: 查询项目的 project_id 列表
        """
        return self._client.request_json("POST",
                                         self._config.list_projects_by_ids,
                                         json={'project_ids': project_ids})

    @response_handler()
    def list_namespaces_in_shared_cluster(self, cluster_id: str) -> Dict:
        url = self._config.list_namespaces_in_shared_cluster.format(
            cluster_id=cluster_id)
        # TODO 支持分页查询
        return self._client.request_json("GET",
                                         url,
                                         params={
                                             'offset': 0,
                                             'limit': 1000
                                         })

    @response_handler(default=[])
    def list_all_projects(self) -> Dict:
        return self._client.request_json('GET',
                                         url=self._config.list_projects,
                                         params={'desire_all_data': 1})
Exemplo n.º 8
0
 def test_request_json(self, requests_mock):
     requests_mock.get('http://test.com', json={'foo': 'bar'})
     client = BaseHttpClient()
     assert client.request_json('GET', 'http://test.com/') == {'foo': 'bar'}
Exemplo n.º 9
0
class PaaSCCClient(BkApiClient):
    """访问 PaaSCC 服务的 Client 对象

    :param auth: 包含校验信息的对象
    """

    def __init__(self, auth: ComponentAuth):
        self._config = PaaSCCConfig(host=BCS_CC_API_PRE_URL)
        self._client = BaseHttpClient(auth.to_header_api_auth())

    def get_cluster(self, project_id: str, cluster_id: str) -> Dict:
        """获取集群信息"""
        url = self._config.get_cluster_url.format(project_id=project_id, cluster_id=cluster_id)
        return self._client.request_json('GET', url)

    @parse_response_data()
    def get_project(self, project_id: str) -> Dict:
        """获取项目信息"""
        url = self._config.get_project_url.format(project_id=project_id)
        return self._client.request_json('GET', url)

    @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,名称、描述等
        """
        url = self._config.update_cluster_url.format(project_id=project_id, cluster_id=cluster_id)
        return self._client.request_json("PUT", url, json=data)

    @response_handler()
    def delete_cluster(self, project_id: str, cluster_id: str):
        """删除集群

        :param project_id: 项目32为长度 ID
        :param cluster_id: 集群ID
        """
        url = self._config.delete_cluster_url.format(project_id=project_id, cluster_id=cluster_id)
        return self._client.request_json("DELETE", url)

    @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和状态
        :returns: 返回更新的节点信息
        """
        url = self._config.update_node_list_url.format(project_id=project_id, cluster_id=cluster_id)
        return self._client.request_json("PATCH", url, json={"updates": [asdict(node) for node in nodes]})

    @response_handler()
    def get_cluster_namespace_list(
        self,
        project_id: str,
        cluster_id: str,
        limit=None,
        offset=None,
        with_lb: Union[bool, int] = False,
        desire_all_data: Union[bool, int] = None,
    ) -> Dict[str, Union[int, List[Dict]]]:
        """获取集群下命名空间列表

        :param project_id: 项目ID
        :param cluster_id: 集群ID
        :param limit: 每页的数量
        :param offset: 第几页
        :param with_lb: 是否返回lb,兼容了bool和int型
        :param desire_all_data: 是否拉取集群下全量命名空间,兼容bool和int型
        :returns: 返回集群下的命名空间
        """
        url = self._config.get_cluster_namespace_list_url.format(project_id=project_id, cluster_id=cluster_id)
        req_params = {"desire_all_data": desire_all_data}
        # NOTE: 根据上层调用,希望获取的是集群下的所有命名空间,因此,当desire_all_data为None时,设置为拉取全量
        if desire_all_data is None:
            req_params["desire_all_data"] = 1
        if limit:
            req_params["limit"] = limit
        if offset:
            req_params["offset"] = offset
        if with_lb:
            req_params["with_lb"] = with_lb

        return self._client.request_json("GET", url, params=req_params)