dts-dance 0.1.9__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dts_dance-0.1.9.dist-info → dts_dance-0.2.0.dist-info}/METADATA +1 -1
- dts_dance-0.2.0.dist-info/RECORD +14 -0
- dtsdance/bytecloud.py +40 -34
- dtsdance/dflow.py +25 -48
- dtsdance/dsyncer.py +48 -48
- dtsdance/metrics_fe.py +13 -13
- dtsdance/spacex.py +46 -47
- dtsdance/tcc_inner.py +55 -0
- dtsdance/{tcc.py → tcc_open.py} +15 -12
- dts_dance-0.1.9.dist-info/RECORD +0 -13
- {dts_dance-0.1.9.dist-info → dts_dance-0.2.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
dtsdance/__init__.py,sha256=Yl_jEZ5weYfcrklnDvwB4wSgCOvMBLRRgWx0gHs3qfM,49
|
|
2
|
+
dtsdance/bytecloud.py,sha256=SN82KIYAfq16s06-HUoj37PbgSbUa7MoFFcah2kaXOk,5213
|
|
3
|
+
dtsdance/dflow.py,sha256=WtD3AbDgGRraddJIxzZs8Bd6Ts56XtqJcjb0UrhnWFQ,5097
|
|
4
|
+
dtsdance/dsyncer.py,sha256=X59sKDvK6rDeumCajepcqSSJysDfvJByYZAhcdOyNLw,11179
|
|
5
|
+
dtsdance/feishu_base.py,sha256=2j4ZM4PFqJ-9EhC6DQ1OmAg--3VBGZyyRuxyjL0j6OU,3733
|
|
6
|
+
dtsdance/feishu_table.py,sha256=ZUeoKrM4nmm5hFhc3vWOVYeLP390orm-284Od92G4iQ,8424
|
|
7
|
+
dtsdance/metrics_fe.py,sha256=hzIl5BJmuCrkqJOHELVzXm3YAqrPttbyVkKBglS4mgQ,18978
|
|
8
|
+
dtsdance/s3.py,sha256=Bh-cwLksfO5PewNtIzE_Md3rRLDLI1DUVoOD7Pou5T8,1294
|
|
9
|
+
dtsdance/spacex.py,sha256=lVhDfGzFZsfeYhWGT-8AE3JM5ZksJdBWRQ-GRL0ymqY,2342
|
|
10
|
+
dtsdance/tcc_inner.py,sha256=xy3N1N3BNl1oc2oMNHKhnsj47rAKOOJ-aXw8GtA2De8,1621
|
|
11
|
+
dtsdance/tcc_open.py,sha256=wjUh56_CDb15s0qXw61fb5JehgIVcySUf0hTaF7V92Y,6674
|
|
12
|
+
dts_dance-0.2.0.dist-info/METADATA,sha256=7IBiJk2ubNRRx2p6gjWnSxqYXa1PzQ_HLTf3DFwSkCI,793
|
|
13
|
+
dts_dance-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
14
|
+
dts_dance-0.2.0.dist-info/RECORD,,
|
dtsdance/bytecloud.py
CHANGED
|
@@ -6,27 +6,29 @@ from loguru import logger
|
|
|
6
6
|
from typing import NamedTuple
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class SiteConfig(NamedTuple):
|
|
10
10
|
name: str
|
|
11
11
|
endpoint: str
|
|
12
|
-
|
|
12
|
+
svc_account: str
|
|
13
|
+
svc_secret: str
|
|
14
|
+
endpoint_bytedts_spacex: str | None = None
|
|
13
15
|
|
|
14
16
|
|
|
15
|
-
class
|
|
17
|
+
class ByteCloudClient:
|
|
16
18
|
"""
|
|
17
|
-
ByteCloud
|
|
19
|
+
ByteCloud Client
|
|
18
20
|
"""
|
|
19
21
|
|
|
20
22
|
# 每小时刷新一次,单位为秒
|
|
21
23
|
_REFRESH_INTERVAL = 1 * 60 * 60
|
|
22
24
|
|
|
23
|
-
def __init__(self,
|
|
25
|
+
def __init__(self, sites: dict[str, SiteConfig]):
|
|
24
26
|
"""
|
|
25
|
-
初始化 ByteCloud
|
|
27
|
+
初始化 ByteCloud Client
|
|
26
28
|
从配置文件加载所有环境的信息,并为每个环境初始化 JWT 令牌
|
|
27
|
-
|
|
29
|
+
sites 中保存内容 list[(name, endpoint, svc_account, svc_secret)]
|
|
28
30
|
"""
|
|
29
|
-
self.
|
|
31
|
+
self.sites = sites
|
|
30
32
|
|
|
31
33
|
# 初始化线程锁,用于保护 jwt_tokens 的并发访问
|
|
32
34
|
self.token_lock = threading.Lock()
|
|
@@ -66,20 +68,20 @@ class ByteCloudHelper:
|
|
|
66
68
|
"""
|
|
67
69
|
logger.debug("开始刷新所有环境的 JWT 令牌...")
|
|
68
70
|
|
|
69
|
-
for _,
|
|
71
|
+
for _, site in self.sites.items():
|
|
70
72
|
try:
|
|
71
73
|
# 刷新令牌
|
|
72
|
-
new_token = self._acquire_jwt_token(
|
|
74
|
+
new_token = self._acquire_jwt_token(site.endpoint, site.svc_secret)
|
|
73
75
|
# 使用线程锁更新缓存中的 JWT 令牌
|
|
74
76
|
with self.token_lock:
|
|
75
|
-
self.jwt_tokens[
|
|
76
|
-
logger.debug(f"环境 {
|
|
77
|
+
self.jwt_tokens[site.name] = new_token
|
|
78
|
+
logger.debug(f"环境 {site.name} 的 JWT 令牌成功刷新,新令牌: {new_token}")
|
|
77
79
|
except Exception as e:
|
|
78
|
-
logger.error(f"环境 {
|
|
80
|
+
logger.error(f"环境 {site.name} 的 JWT 令牌刷新失败: {e}")
|
|
79
81
|
|
|
80
82
|
logger.debug(f"所有环境的 JWT 令牌已成功刷新。jwt_tokens: {self.jwt_tokens}")
|
|
81
83
|
|
|
82
|
-
def _acquire_jwt_token(self, endpoint: str,
|
|
84
|
+
def _acquire_jwt_token(self, endpoint: str, svc_secret: str) -> str:
|
|
83
85
|
"""
|
|
84
86
|
获取 JWT 令牌
|
|
85
87
|
|
|
@@ -93,7 +95,7 @@ class ByteCloudHelper:
|
|
|
93
95
|
url = endpoint + "/auth/api/v1/jwt"
|
|
94
96
|
headers = {
|
|
95
97
|
"Content-Type": "application/json",
|
|
96
|
-
"Authorization": "Bearer " +
|
|
98
|
+
"Authorization": "Bearer " + svc_secret,
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
try:
|
|
@@ -125,39 +127,43 @@ class ByteCloudHelper:
|
|
|
125
127
|
logger.error(f"获取JWT令牌时出错: {e}")
|
|
126
128
|
raise
|
|
127
129
|
|
|
128
|
-
def get_jwt_token(self,
|
|
130
|
+
def get_jwt_token(self, site: str) -> str:
|
|
129
131
|
"""
|
|
130
|
-
|
|
132
|
+
获取指定站点的 JWT 令牌
|
|
133
|
+
"""
|
|
134
|
+
# 使用线程锁保护并发访问
|
|
135
|
+
with self.token_lock:
|
|
136
|
+
if site not in self.jwt_tokens:
|
|
137
|
+
raise KeyError(f"站点 {site} 的 JWT 令牌不存在")
|
|
138
|
+
return self.jwt_tokens[site]
|
|
139
|
+
|
|
140
|
+
def build_request_headers(self, site: str) -> dict[str, str]:
|
|
141
|
+
"""
|
|
142
|
+
构建请求头
|
|
131
143
|
|
|
132
144
|
Args:
|
|
133
|
-
|
|
145
|
+
site: 站点名称
|
|
134
146
|
|
|
135
147
|
Returns:
|
|
136
|
-
str:
|
|
137
|
-
|
|
138
|
-
Raises:
|
|
139
|
-
KeyError: 如果指定的环境不存在
|
|
148
|
+
dict[str, str]: 请求头字典
|
|
140
149
|
"""
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if env not in self.jwt_tokens:
|
|
144
|
-
raise KeyError(f"环境 {env} 的 JWT 令牌不存在")
|
|
145
|
-
return self.jwt_tokens[env]
|
|
150
|
+
jwt_token = self.get_jwt_token(site)
|
|
151
|
+
return {"Content-Type": "application/json", "x-jwt-token": jwt_token}
|
|
146
152
|
|
|
147
|
-
def
|
|
153
|
+
def get_site_info(self, site: str) -> SiteConfig:
|
|
148
154
|
"""
|
|
149
155
|
获取指定环境的信息
|
|
150
156
|
|
|
151
157
|
Args:
|
|
152
|
-
|
|
158
|
+
site: 站点名称
|
|
153
159
|
|
|
154
160
|
Returns:
|
|
155
|
-
|
|
161
|
+
SiteConfig: 指定站点的信息
|
|
156
162
|
|
|
157
163
|
Raises:
|
|
158
|
-
KeyError:
|
|
164
|
+
KeyError: 如果指定的站点不存在
|
|
159
165
|
"""
|
|
160
|
-
if
|
|
161
|
-
raise KeyError(f"
|
|
166
|
+
if site not in self.sites:
|
|
167
|
+
raise KeyError(f"站点 {site} 不存在")
|
|
162
168
|
|
|
163
|
-
return self.
|
|
169
|
+
return self.sites[site]
|
dtsdance/dflow.py
CHANGED
|
@@ -1,27 +1,13 @@
|
|
|
1
1
|
from typing import Any, cast, Optional
|
|
2
2
|
from loguru import logger
|
|
3
|
-
from .bytecloud import
|
|
3
|
+
from .bytecloud import ByteCloudClient
|
|
4
4
|
import requests
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
class
|
|
7
|
+
class DFlowClient:
|
|
8
8
|
|
|
9
|
-
def __init__(self,
|
|
10
|
-
self.
|
|
11
|
-
|
|
12
|
-
def _build_headers(self, env: str) -> dict[str, str]:
|
|
13
|
-
"""
|
|
14
|
-
构建请求头
|
|
15
|
-
|
|
16
|
-
Args:
|
|
17
|
-
env: 环境名称
|
|
18
|
-
|
|
19
|
-
Returns:
|
|
20
|
-
dict[str, str]: 请求头字典
|
|
21
|
-
"""
|
|
22
|
-
jwt_token = self.bytecloud_helper.get_jwt_token(env)
|
|
23
|
-
headers = {"x-jwt-token": jwt_token}
|
|
24
|
-
return headers
|
|
9
|
+
def __init__(self, bytecloud_client: ByteCloudClient) -> None:
|
|
10
|
+
self.bytecloud_client = bytecloud_client
|
|
25
11
|
|
|
26
12
|
def _make_request(self, method: str, url: str, headers: dict[str, str], json_data: Optional[dict] = None) -> dict[str, Any]:
|
|
27
13
|
"""
|
|
@@ -58,30 +44,27 @@ class DFlowHelper:
|
|
|
58
44
|
logger.warning(error_msg)
|
|
59
45
|
raise
|
|
60
46
|
|
|
61
|
-
def get_dflow_info(self,
|
|
47
|
+
def get_dflow_info(self, site: str, task_id: str) -> dict[str, Any]:
|
|
62
48
|
"""
|
|
63
49
|
获取 DFlow 任务信息
|
|
64
50
|
|
|
65
51
|
Args:
|
|
66
|
-
|
|
52
|
+
site: 站点名称
|
|
67
53
|
task_id: DFlow 任务 ID
|
|
68
54
|
|
|
69
55
|
Returns:
|
|
70
56
|
dict[str, Any]: DFlow 任务信息,包含 create_time 等字段
|
|
71
57
|
"""
|
|
72
58
|
# 构建 API URL
|
|
73
|
-
|
|
74
|
-
url = f"{
|
|
75
|
-
|
|
76
|
-
# 准备请求头
|
|
77
|
-
headers = self._build_headers(env)
|
|
59
|
+
site_info = self.bytecloud_client.get_site_info(site)
|
|
60
|
+
url = f"{site_info.endpoint}/api/v1/bytedts/api/bytedts/v3/DescribeTaskInfo"
|
|
78
61
|
|
|
79
62
|
# 构建请求数据
|
|
80
63
|
json_data = {"id": int(task_id)}
|
|
81
64
|
|
|
82
|
-
response_data = self._make_request("POST", url,
|
|
65
|
+
response_data = self._make_request("POST", url, self.bytecloud_client.build_request_headers(site), json_data)
|
|
83
66
|
|
|
84
|
-
logger.info(f"get_dflow_info {
|
|
67
|
+
logger.info(f"get_dflow_info {site} {task_id}, message: {response_data.get('message')}")
|
|
85
68
|
|
|
86
69
|
try:
|
|
87
70
|
data = cast(dict, response_data.get("data", {}))
|
|
@@ -99,71 +82,65 @@ class DFlowHelper:
|
|
|
99
82
|
except (KeyError, AttributeError, Exception) as e:
|
|
100
83
|
raise Exception(f"无法从响应中提取 DFlow 任务信息数据: {str(e)}")
|
|
101
84
|
|
|
102
|
-
def generate_task_url(self,
|
|
85
|
+
def generate_task_url(self, site: str, task_id: str) -> str:
|
|
103
86
|
"""
|
|
104
87
|
获取 DFlow 任务详情页面的 URL
|
|
105
88
|
|
|
106
89
|
Args:
|
|
107
|
-
|
|
90
|
+
site: 站点名称
|
|
108
91
|
task_id: DFlow 任务 ID
|
|
109
92
|
|
|
110
93
|
Returns:
|
|
111
94
|
str: DFlow 任务详情页面的 URL
|
|
112
95
|
"""
|
|
113
96
|
# 根据环境生成对应的 scope 参数
|
|
114
|
-
|
|
115
|
-
return f"{
|
|
97
|
+
site_info = self.bytecloud_client.get_site_info(site)
|
|
98
|
+
return f"{site_info.endpoint}/bytedts/datasync/detail/{task_id}?scope={site}"
|
|
116
99
|
|
|
117
|
-
def init_resources(self,
|
|
100
|
+
def init_resources(self, site: str, ctrl_env: str) -> bool:
|
|
118
101
|
"""
|
|
119
102
|
初始化 CTRL 环境资源
|
|
120
103
|
|
|
121
104
|
Args:
|
|
122
|
-
|
|
105
|
+
site: 站点名称
|
|
123
106
|
ctrl_env: 控制环境
|
|
124
107
|
|
|
125
108
|
Returns:
|
|
126
109
|
bool: CTRL 环境资源初始化结果
|
|
127
110
|
"""
|
|
128
111
|
# 构建 API URL
|
|
129
|
-
|
|
130
|
-
url = f"{
|
|
131
|
-
|
|
132
|
-
# 准备请求头
|
|
133
|
-
headers = self._build_headers(env)
|
|
112
|
+
site_info = self.bytecloud_client.get_site_info(site)
|
|
113
|
+
url = f"{site_info.endpoint}/api/v1/bytedts/api/bytedts/v3/InitSystemResource"
|
|
134
114
|
|
|
135
115
|
# 构建请求数据
|
|
136
116
|
json_data = {"ctrl_env": ctrl_env}
|
|
137
117
|
|
|
138
|
-
response_data = self._make_request("POST", url,
|
|
118
|
+
response_data = self._make_request("POST", url, self.bytecloud_client.build_request_headers(site), json_data)
|
|
139
119
|
|
|
140
120
|
message = response_data.get("message")
|
|
141
|
-
logger.info(f"int_resources {
|
|
121
|
+
logger.info(f"int_resources {site} {ctrl_env}, message: {message}")
|
|
142
122
|
|
|
143
123
|
return message == "ok"
|
|
144
124
|
|
|
145
|
-
def list_resources(self,
|
|
125
|
+
def list_resources(self, site: str, ctrl_env: str) -> list[str]:
|
|
146
126
|
"""
|
|
147
127
|
列举 CTRL 环境资源列表
|
|
148
128
|
|
|
149
129
|
Args:
|
|
150
|
-
|
|
130
|
+
site: 站点名称
|
|
151
131
|
ctrl_env: 控制环境
|
|
152
132
|
|
|
153
133
|
Returns:
|
|
154
134
|
list[str]: CTRL 环境资源列表
|
|
155
135
|
"""
|
|
156
136
|
# 构建 API URL
|
|
157
|
-
|
|
158
|
-
url = f"{
|
|
159
|
-
|
|
160
|
-
# 准备请求头
|
|
161
|
-
headers = self._build_headers(env)
|
|
137
|
+
site_info = self.bytecloud_client.get_site_info(site)
|
|
138
|
+
url = f"{site_info.endpoint}/api/v1/bytedts/api/bytedts/v3/DescribeResources"
|
|
162
139
|
|
|
163
140
|
# 构建请求数据
|
|
164
141
|
json_data = {"offset": 0, "limit": 10, "ctrl_env": ctrl_env}
|
|
165
142
|
|
|
166
|
-
response_data = self._make_request("POST", url,
|
|
143
|
+
response_data = self._make_request("POST", url, self.bytecloud_client.build_request_headers(site), json_data)
|
|
167
144
|
|
|
168
145
|
try:
|
|
169
146
|
data = cast(dict, response_data.get("data", {}))
|
dtsdance/dsyncer.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from .bytecloud import
|
|
1
|
+
from .bytecloud import ByteCloudClient
|
|
2
2
|
from typing import Any, NamedTuple, Tuple, cast, Optional
|
|
3
3
|
from loguru import logger
|
|
4
4
|
import requests
|
|
@@ -15,11 +15,11 @@ class DSyncerEnvInfo(NamedTuple):
|
|
|
15
15
|
token: str
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
class
|
|
18
|
+
class DSyncerClient:
|
|
19
19
|
|
|
20
|
-
def __init__(self, envs: dict[str, DSyncerEnvInfo],
|
|
20
|
+
def __init__(self, envs: dict[str, DSyncerEnvInfo], bytecloud_client: ByteCloudClient) -> None:
|
|
21
21
|
self.envs = envs
|
|
22
|
-
self.
|
|
22
|
+
self.bytecloud_client = bytecloud_client
|
|
23
23
|
|
|
24
24
|
def _build_headers(self, env: str, secret_api: bool = False) -> dict[str, str]:
|
|
25
25
|
"""
|
|
@@ -32,7 +32,7 @@ class DSyncerHelper:
|
|
|
32
32
|
Returns:
|
|
33
33
|
dict[str, str]: 请求头字典
|
|
34
34
|
"""
|
|
35
|
-
jwt_token = self.
|
|
35
|
+
jwt_token = self.bytecloud_client.get_jwt_token(env)
|
|
36
36
|
headers = {"X-Jwt-Token": jwt_token, "x-bcgw-vregion": env}
|
|
37
37
|
|
|
38
38
|
if secret_api:
|
|
@@ -41,7 +41,7 @@ class DSyncerHelper:
|
|
|
41
41
|
|
|
42
42
|
return headers
|
|
43
43
|
|
|
44
|
-
def _make_request(self, method: str, url: str, headers: dict[str, str],
|
|
44
|
+
def _make_request(self, method: str, url: str, headers: dict[str, str], data_raw: Optional[dict] = None) -> dict[str, Any]:
|
|
45
45
|
"""
|
|
46
46
|
发送 HTTP 请求的通用方法
|
|
47
47
|
|
|
@@ -60,7 +60,7 @@ class DSyncerHelper:
|
|
|
60
60
|
if method.upper() == "GET":
|
|
61
61
|
response = requests.get(url, headers=headers)
|
|
62
62
|
elif method.upper() == "POST":
|
|
63
|
-
response = requests.post(url, json=
|
|
63
|
+
response = requests.post(url, json=data_raw, headers=headers)
|
|
64
64
|
else:
|
|
65
65
|
raise ValueError(f"不支持的 HTTP 方法: {method}")
|
|
66
66
|
|
|
@@ -77,41 +77,41 @@ class DSyncerHelper:
|
|
|
77
77
|
logger.warning(error_msg)
|
|
78
78
|
raise
|
|
79
79
|
|
|
80
|
-
def _acquire_task_info(self,
|
|
80
|
+
def _acquire_task_info(self, site: str, task_id: str) -> dict[str, str]:
|
|
81
81
|
"""
|
|
82
82
|
获取 DSyncer 任务信息
|
|
83
83
|
|
|
84
84
|
Args:
|
|
85
|
-
|
|
85
|
+
site: 站点名称
|
|
86
86
|
task_id: DSyncer 任务 ID
|
|
87
87
|
|
|
88
88
|
Returns:
|
|
89
89
|
dict[str, str]: DSyncer 任务的 rocket_mq_connection 信息,只包含 cluster、topic 和 group 字段
|
|
90
90
|
"""
|
|
91
91
|
# 构建 API URL
|
|
92
|
-
|
|
93
|
-
url = f"{
|
|
92
|
+
site_info = self.bytecloud_client.get_site_info(site)
|
|
93
|
+
url = f"{site_info.endpoint}/api/v1/dsyncer/openapi/taskinfo/{task_id}/"
|
|
94
94
|
|
|
95
95
|
# 准备请求头
|
|
96
|
-
headers = self._build_headers(
|
|
96
|
+
headers = self._build_headers(site)
|
|
97
97
|
|
|
98
98
|
return self._make_request("GET", url, headers)
|
|
99
99
|
|
|
100
|
-
def get_dflow_task_info(self,
|
|
100
|
+
def get_dflow_task_info(self, site: str, task_id: str) -> tuple[str, str]:
|
|
101
101
|
"""
|
|
102
102
|
获取迁移后的 DFlow 任务信息
|
|
103
103
|
"""
|
|
104
104
|
# 构建 API URL
|
|
105
|
-
|
|
106
|
-
url = f"{
|
|
105
|
+
site_info = self.bytecloud_client.get_site_info(site)
|
|
106
|
+
url = f"{site_info.endpoint}/api/v1/dsyncer/openapi/taskinfo/{task_id}/migrate/"
|
|
107
107
|
|
|
108
108
|
# 准备请求头
|
|
109
|
-
headers = self._build_headers(
|
|
109
|
+
headers = self._build_headers(site)
|
|
110
110
|
|
|
111
111
|
json_data = self._make_request("GET", url, headers)
|
|
112
112
|
|
|
113
113
|
message = json_data.get("message", "")
|
|
114
|
-
logger.debug(f"get task migrate info {
|
|
114
|
+
logger.debug(f"get task migrate info {site} {task_id}, message: {message}")
|
|
115
115
|
|
|
116
116
|
# 从消息中提取 DFlow 任务 URL 和 ID
|
|
117
117
|
# 消息格式: "任务已迁移至ByteDTS平台,请前往[https://cloud.bytedance.net/bytedts/datasync/detail/93127366537986?scope=China-North&tabKey=DetailInfo]查看详情"
|
|
@@ -133,7 +133,7 @@ class DSyncerHelper:
|
|
|
133
133
|
logger.warning(f"could not extract dflow task info from message: {message}")
|
|
134
134
|
return ("", "")
|
|
135
135
|
|
|
136
|
-
def generate_task_url(self,
|
|
136
|
+
def generate_task_url(self, site: str, task_id: str) -> str:
|
|
137
137
|
"""
|
|
138
138
|
获取 DSyncer 任务详情页面的 URL
|
|
139
139
|
|
|
@@ -144,8 +144,8 @@ class DSyncerHelper:
|
|
|
144
144
|
Returns:
|
|
145
145
|
str: DSyncer 任务详情页面的 URL
|
|
146
146
|
"""
|
|
147
|
-
|
|
148
|
-
return DSyncer_Task_Detail_URL.format(endpoint=
|
|
147
|
+
site_info = self.bytecloud_client.get_site_info(site)
|
|
148
|
+
return DSyncer_Task_Detail_URL.format(endpoint=site_info.endpoint, task_id=task_id)
|
|
149
149
|
|
|
150
150
|
def generate_task_grafana_url(self, task_id: str, change_time: str) -> str:
|
|
151
151
|
"""
|
|
@@ -200,7 +200,7 @@ class DSyncerHelper:
|
|
|
200
200
|
except (KeyError, AttributeError, Exception) as e:
|
|
201
201
|
raise Exception(f"无法从响应中提取 DSyncer 任务状态数据: {str(e)}")
|
|
202
202
|
|
|
203
|
-
def is_task_migrate_running(self,
|
|
203
|
+
def is_task_migrate_running(self, site: str, task_id: str) -> Tuple[bool, str]:
|
|
204
204
|
"""
|
|
205
205
|
检查 DSyncer 任务是否正在迁移中
|
|
206
206
|
|
|
@@ -208,62 +208,62 @@ class DSyncerHelper:
|
|
|
208
208
|
bool: 如果任务正在迁移中,返回 True;否则返回 False
|
|
209
209
|
"""
|
|
210
210
|
# 构建 API URL
|
|
211
|
-
|
|
212
|
-
url = f"{
|
|
211
|
+
site_info = self.bytecloud_client.get_site_info(site)
|
|
212
|
+
url = f"{site_info.endpoint}/api/v1/dsyncer/secret_api/task/migrate/check"
|
|
213
213
|
|
|
214
214
|
# 准备请求头
|
|
215
|
-
headers = self._build_headers(
|
|
215
|
+
headers = self._build_headers(site, secret_api=True)
|
|
216
216
|
|
|
217
|
-
|
|
217
|
+
data_raw = {"task_id": task_id}
|
|
218
218
|
|
|
219
|
-
response_data = self._make_request("POST", url, headers,
|
|
219
|
+
response_data = self._make_request("POST", url, headers, data_raw)
|
|
220
220
|
|
|
221
221
|
message = response_data.get("message", "")
|
|
222
|
-
logger.debug(f"get task migrate status {
|
|
222
|
+
logger.debug(f"get task migrate status {site} {task_id}, message: {message}")
|
|
223
223
|
if "task migrate is running" in message:
|
|
224
224
|
return True, ""
|
|
225
225
|
else:
|
|
226
226
|
return False, response_data.get("data", {}).get("msg", {})
|
|
227
227
|
|
|
228
|
-
def migration_rollback(self,
|
|
228
|
+
def migration_rollback(self, site: str, task_id: str) -> bool:
|
|
229
229
|
"""
|
|
230
230
|
执行回滚
|
|
231
231
|
"""
|
|
232
232
|
# 构建 API URL
|
|
233
|
-
|
|
234
|
-
url = f"{
|
|
233
|
+
site_info = self.bytecloud_client.get_site_info(site)
|
|
234
|
+
url = f"{site_info.endpoint}/api/v1/dsyncer/secret_api/task/rollback_migrate2dsyncer/"
|
|
235
235
|
|
|
236
236
|
# 准备请求头
|
|
237
|
-
headers = self._build_headers(
|
|
237
|
+
headers = self._build_headers(site, secret_api=True)
|
|
238
238
|
|
|
239
|
-
|
|
239
|
+
data_raw = {"task_id_list": [task_id]}
|
|
240
240
|
|
|
241
|
-
response_data = self._make_request("POST", url, headers,
|
|
241
|
+
response_data = self._make_request("POST", url, headers, data_raw)
|
|
242
242
|
|
|
243
|
-
logger.debug(f"migration_mark_rollback return {
|
|
243
|
+
logger.debug(f"migration_mark_rollback return {site} {task_id}, json_data: {response_data}")
|
|
244
244
|
success_task = response_data.get("data", {}).get("success_task", [])
|
|
245
245
|
return task_id in success_task
|
|
246
246
|
|
|
247
|
-
def migration_mark_success(self,
|
|
247
|
+
def migration_mark_success(self, site: str, task_id: str) -> bool:
|
|
248
248
|
"""
|
|
249
249
|
标记迁移成功
|
|
250
250
|
"""
|
|
251
251
|
# 构建 API URL
|
|
252
|
-
|
|
253
|
-
url = f"{
|
|
252
|
+
site_info = self.bytecloud_client.get_site_info(site)
|
|
253
|
+
url = f"{site_info.endpoint}/api/v1/dsyncer/secret_api/task/mark_migrate_success/"
|
|
254
254
|
|
|
255
255
|
# 准备请求头
|
|
256
|
-
headers = self._build_headers(
|
|
256
|
+
headers = self._build_headers(site, secret_api=True)
|
|
257
257
|
|
|
258
|
-
|
|
258
|
+
data_raw = {"task_id": task_id}
|
|
259
259
|
|
|
260
|
-
response_data = self._make_request("POST", url, headers,
|
|
260
|
+
response_data = self._make_request("POST", url, headers, data_raw)
|
|
261
261
|
|
|
262
|
-
logger.debug(f"migration_mark_success return {
|
|
262
|
+
logger.debug(f"migration_mark_success return {site} {task_id}, json_data: {response_data}")
|
|
263
263
|
success_task = response_data.get("data", {}).get("success_task", [])
|
|
264
264
|
return task_id in success_task
|
|
265
265
|
|
|
266
|
-
def migrate_task(self,
|
|
266
|
+
def migrate_task(self, site: str, task_id: str, app_parallel: int) -> Optional[str]:
|
|
267
267
|
"""
|
|
268
268
|
迁移任务到DFlow
|
|
269
269
|
|
|
@@ -271,13 +271,13 @@ class DSyncerHelper:
|
|
|
271
271
|
Optional[str]: 错误信息,成功时返回None,失败时返回错误信息
|
|
272
272
|
"""
|
|
273
273
|
# 构建 API URL
|
|
274
|
-
|
|
275
|
-
url = f"{
|
|
274
|
+
site_info = self.bytecloud_client.get_site_info(site)
|
|
275
|
+
url = f"{site_info.endpoint}/api/v1/dsyncer/secret_api/task/migrate2dflow/single"
|
|
276
276
|
|
|
277
277
|
# 准备请求头
|
|
278
|
-
headers = self._build_headers(
|
|
278
|
+
headers = self._build_headers(site, secret_api=True)
|
|
279
279
|
|
|
280
|
-
|
|
280
|
+
data_raw = {
|
|
281
281
|
"task_id": task_id,
|
|
282
282
|
"delay_threshold": "20s",
|
|
283
283
|
"dsyncer_task_pause_threshold": "180s",
|
|
@@ -289,9 +289,9 @@ class DSyncerHelper:
|
|
|
289
289
|
"app_parallel": app_parallel,
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
-
response_data = self._make_request("POST", url, headers,
|
|
292
|
+
response_data = self._make_request("POST", url, headers, data_raw)
|
|
293
293
|
|
|
294
|
-
logger.debug(f"migrate_task return {
|
|
294
|
+
logger.debug(f"migrate_task return {site} {task_id}, json_data: {response_data}")
|
|
295
295
|
message = response_data.get("message")
|
|
296
296
|
if response_data.get("code") == 0 and message == "ok":
|
|
297
297
|
err_message = None
|
dtsdance/metrics_fe.py
CHANGED
|
@@ -2,7 +2,7 @@ from loguru import logger
|
|
|
2
2
|
import requests
|
|
3
3
|
import threading
|
|
4
4
|
import time
|
|
5
|
-
from typing import
|
|
5
|
+
from typing import NamedTuple, Optional
|
|
6
6
|
from datetime import datetime, timedelta
|
|
7
7
|
|
|
8
8
|
|
|
@@ -60,9 +60,9 @@ def get_metric_type_by_name(metric_name: str) -> Optional[MetricType]:
|
|
|
60
60
|
return None
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
class
|
|
63
|
+
class MetricsClient:
|
|
64
64
|
"""
|
|
65
|
-
Metrics
|
|
65
|
+
Metrics Client
|
|
66
66
|
"""
|
|
67
67
|
|
|
68
68
|
# https://cloud.bytedance.net/docs/metrics/docs/63bbbb1ec6b537022a6000c7/63bbd4e8777a300220d4ac3a?x-resource-account=public&x-bc-region-id=bytedance
|
|
@@ -71,7 +71,7 @@ class MetricsHelper:
|
|
|
71
71
|
|
|
72
72
|
def __init__(self, envs: dict[str, MetricEnvInfo]):
|
|
73
73
|
"""
|
|
74
|
-
初始化 Metrics
|
|
74
|
+
初始化 Metrics Client
|
|
75
75
|
从配置文件加载所有环境的信息,并为每个环境初始化访问令牌
|
|
76
76
|
"""
|
|
77
77
|
self.envs = envs
|
|
@@ -80,7 +80,7 @@ class MetricsHelper:
|
|
|
80
80
|
self.token_lock = threading.Lock()
|
|
81
81
|
|
|
82
82
|
# 初始化访问令牌缓存,按环境名称索引
|
|
83
|
-
self.tokens:
|
|
83
|
+
self.tokens: dict[str, str] = {}
|
|
84
84
|
|
|
85
85
|
# 更新所有环境的配置信息
|
|
86
86
|
self._refresh_tokens()
|
|
@@ -212,8 +212,8 @@ class MetricsHelper:
|
|
|
212
212
|
return self.envs[env_name].endpoint
|
|
213
213
|
|
|
214
214
|
def _build_query_payload(
|
|
215
|
-
self, metric_name: str, aggregator: str, start_time_ms: int, end_time_ms: int, filters:
|
|
216
|
-
) ->
|
|
215
|
+
self, metric_name: str, aggregator: str, start_time_ms: int, end_time_ms: int, filters: list[dict], rate: bool, **kwargs
|
|
216
|
+
) -> dict:
|
|
217
217
|
"""
|
|
218
218
|
构建查询请求体
|
|
219
219
|
|
|
@@ -254,8 +254,8 @@ class MetricsHelper:
|
|
|
254
254
|
}
|
|
255
255
|
|
|
256
256
|
def _execute_metrics_query(
|
|
257
|
-
self, env: str, metric_name: str, aggregator: str, start_time: str, end_time: str, filters:
|
|
258
|
-
) ->
|
|
257
|
+
self, env: str, metric_name: str, aggregator: str, start_time: str, end_time: str, filters: list[dict], rate: bool, **kwargs
|
|
258
|
+
) -> dict:
|
|
259
259
|
"""
|
|
260
260
|
执行通用的指标查询
|
|
261
261
|
|
|
@@ -318,7 +318,7 @@ class MetricsHelper:
|
|
|
318
318
|
|
|
319
319
|
def get_mq_group_metrics(
|
|
320
320
|
self, env: str, cluster: str, topic: str, group: str, start_time: str, end_time: str, metric_type: MetricType, **kwargs
|
|
321
|
-
) ->
|
|
321
|
+
) -> dict:
|
|
322
322
|
"""
|
|
323
323
|
获取指定时间段的堆积指标
|
|
324
324
|
|
|
@@ -398,7 +398,7 @@ class MetricsHelper:
|
|
|
398
398
|
|
|
399
399
|
return int(max_consume_tps)
|
|
400
400
|
|
|
401
|
-
def get_dsyncer_task_metrics(self, env: str, metric_type: MetricType, dsyncer_increment_task_id: str, start_time: str, end_time: str) ->
|
|
401
|
+
def get_dsyncer_task_metrics(self, env: str, metric_type: MetricType, dsyncer_increment_task_id: str, start_time: str, end_time: str) -> dict:
|
|
402
402
|
"""
|
|
403
403
|
获取 DSyncer 任务指定时间段的指标
|
|
404
404
|
|
|
@@ -434,7 +434,7 @@ class MetricsHelper:
|
|
|
434
434
|
logger.error(f"get_task_latency_metrics env: {env}, dsyncer_increment_task_id: {dsyncer_increment_task_id}, error: {e}")
|
|
435
435
|
raise Exception(f"获取指标失败: {e}")
|
|
436
436
|
|
|
437
|
-
def get_dflow_task_metrics(self, env: str, dflow_task_id: str, metric_type: MetricType, start_time: str, end_time: str, **kwargs) ->
|
|
437
|
+
def get_dflow_task_metrics(self, env: str, dflow_task_id: str, metric_type: MetricType, start_time: str, end_time: str, **kwargs) -> dict:
|
|
438
438
|
"""
|
|
439
439
|
获取指定时间段的 DFlow 指标
|
|
440
440
|
|
|
@@ -471,7 +471,7 @@ class MetricsHelper:
|
|
|
471
471
|
raise Exception(f"获取指标失败: {e}")
|
|
472
472
|
|
|
473
473
|
@staticmethod
|
|
474
|
-
def _remove_last_datapoint(end_time: str, dps:
|
|
474
|
+
def _remove_last_datapoint(end_time: str, dps: dict[str, float]) -> dict[str, float]:
|
|
475
475
|
"""
|
|
476
476
|
移除监控数据中最后一个数据点,因为最后一个点往往不准确
|
|
477
477
|
只有当最后一个点的时间戳与 end_time 相差在 15 秒内时才移除
|
dtsdance/spacex.py
CHANGED
|
@@ -1,69 +1,68 @@
|
|
|
1
|
-
from
|
|
1
|
+
from typing import Any, NamedTuple
|
|
2
|
+
from .bytecloud import ByteCloudClient
|
|
2
3
|
import requests
|
|
3
|
-
import json
|
|
4
|
-
from typing import Dict, List
|
|
5
4
|
from loguru import logger
|
|
6
5
|
|
|
7
6
|
|
|
8
|
-
class
|
|
7
|
+
class GatewayInfo(NamedTuple):
|
|
8
|
+
mgr_name: str
|
|
9
|
+
ctrl_name: str
|
|
10
|
+
gateway_endpoint: str
|
|
11
|
+
auth_user: str
|
|
12
|
+
auth_password: str
|
|
13
|
+
root_secret_key: str
|
|
14
|
+
gw_meta_db: str
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SpaceXClient:
|
|
9
18
|
"""
|
|
10
19
|
SpaceX 通知服务客户端,用于发送飞书消息
|
|
11
20
|
"""
|
|
12
21
|
|
|
13
|
-
|
|
14
|
-
_SPACEX_DOMAIN = {
|
|
15
|
-
"BOE": "spacex-api-boe.byted.org",
|
|
16
|
-
"China-North": "spacex-api.byted.org",
|
|
17
|
-
"Singapore-Central": "spacex-api-i18n.byted.org",
|
|
18
|
-
"EUTTP": "spacex-api.tiktoke.org",
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
def __init__(self, bytecloud_helper: ByteCloudHelper):
|
|
22
|
+
def __init__(self, bytecloud_client: ByteCloudClient):
|
|
22
23
|
"""
|
|
23
24
|
初始化 SpaceX 通知客户端
|
|
24
25
|
"""
|
|
25
|
-
self.
|
|
26
|
+
self.bytecloud_client = bytecloud_client
|
|
26
27
|
|
|
27
|
-
def
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
def list_mgr(self, site: str) -> list[Any]:
|
|
29
|
+
site_info = self.bytecloud_client.get_site_info(site)
|
|
30
|
+
url = f"{site_info.endpoint_bytedts_spacex}/bytedts/v1/queryServerMeta"
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
try:
|
|
33
|
+
response = requests.post(url, headers=self.bytecloud_client.build_request_headers(site))
|
|
34
|
+
response.raise_for_status()
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
result = response.json()
|
|
37
|
+
return result.get("data", [])
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
logger.debug(f"send_feishu_message env: {env}, feishu_content: {str(feishu_content)[:100]}..., feishu_groups: {feishu_groups}")
|
|
39
|
+
except Exception as e:
|
|
40
|
+
logger.warning(f"do quest queryServerMeta exception: {e}")
|
|
41
|
+
raise
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
def registry_gateway(self, site: str, gateway_info: GatewayInfo) -> bool:
|
|
44
|
+
site_info = self.bytecloud_client.get_site_info(site)
|
|
45
|
+
url = f"{site_info.endpoint_bytedts_spacex}/bytedts/v1/registryGateway"
|
|
46
|
+
data_raw = {
|
|
47
|
+
"server_region": gateway_info.mgr_name,
|
|
48
|
+
"cluster_region": gateway_info.ctrl_name,
|
|
49
|
+
"cluster_name": gateway_info.ctrl_name,
|
|
50
|
+
"server_domain": gateway_info.gateway_endpoint,
|
|
51
|
+
"frontend_user": gateway_info.auth_user,
|
|
52
|
+
"gateway_user": gateway_info.auth_user,
|
|
53
|
+
"root_secret_key": gateway_info.root_secret_key,
|
|
54
|
+
"gateway_password": gateway_info.auth_password,
|
|
55
|
+
"frontend_password": gateway_info.auth_password,
|
|
56
|
+
"gw_meta_db": gateway_info.gw_meta_db,
|
|
57
|
+
"gateway_type": "psm",
|
|
52
58
|
}
|
|
53
|
-
|
|
54
|
-
# 发送 HTTP POST 请求
|
|
55
59
|
try:
|
|
56
|
-
url =
|
|
57
|
-
headers = {
|
|
58
|
-
"Content-Type": "application/json",
|
|
59
|
-
"X-Jwt-Token": self.bytecloud_helper.get_jwt_token(env),
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
response = requests.post(url, json=body, headers=headers)
|
|
60
|
+
response = requests.post(url, json=data_raw, headers=self.bytecloud_client.build_request_headers(site))
|
|
63
61
|
response.raise_for_status()
|
|
64
62
|
|
|
65
|
-
|
|
66
|
-
return
|
|
63
|
+
result = response.json()
|
|
64
|
+
return result.get("message", "") == "ok"
|
|
65
|
+
|
|
67
66
|
except Exception as e:
|
|
68
|
-
|
|
67
|
+
logger.warning(f"do quest registryGateway exception: {e}")
|
|
69
68
|
raise
|
dtsdance/tcc_inner.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""TCC (Toutiao Config Center) API client."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
from dtsdance.bytecloud import ByteCloudClient
|
|
7
|
+
from dtsdance.tcc_open import TCCError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TCCInnerClient:
|
|
11
|
+
"""Client for TCC OpenAPI."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, bytecloud_client: ByteCloudClient) -> None:
|
|
14
|
+
self.bytecloud_client = bytecloud_client
|
|
15
|
+
|
|
16
|
+
def list_configs(
|
|
17
|
+
self,
|
|
18
|
+
site: str,
|
|
19
|
+
ns_name: str,
|
|
20
|
+
region: str,
|
|
21
|
+
dir: str,
|
|
22
|
+
conf_name: str,
|
|
23
|
+
) -> dict[str, Any]:
|
|
24
|
+
"""
|
|
25
|
+
List TCC configurations.
|
|
26
|
+
"""
|
|
27
|
+
site_info = self.bytecloud_client.get_site_info(site)
|
|
28
|
+
url = f"{site_info.endpoint}/api/v3/tcc/bcc/config/list_v2"
|
|
29
|
+
|
|
30
|
+
data_raw = {
|
|
31
|
+
"ns_name": ns_name,
|
|
32
|
+
"region": region,
|
|
33
|
+
"dir_path": dir,
|
|
34
|
+
"keyword": conf_name,
|
|
35
|
+
"env": "prod",
|
|
36
|
+
"scope": "all",
|
|
37
|
+
"condition": "name",
|
|
38
|
+
"pn": 1,
|
|
39
|
+
"rn": 100,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
response = requests.post(url, headers=self.bytecloud_client.build_request_headers(site), json=data_raw, timeout=30)
|
|
44
|
+
response.raise_for_status()
|
|
45
|
+
result = response.json()
|
|
46
|
+
|
|
47
|
+
# Check for errors in response
|
|
48
|
+
base_resp = result.get("base_resp", {})
|
|
49
|
+
if base_resp.get("error_code", 0) != 0:
|
|
50
|
+
raise TCCError(f"TCC API error: {base_resp.get('error_message', 'Unknown error')}")
|
|
51
|
+
|
|
52
|
+
return result.get("data", {})
|
|
53
|
+
|
|
54
|
+
except requests.RequestException as e:
|
|
55
|
+
raise TCCError(f"Failed to get TCC config: {str(e)}") from e
|
dtsdance/{tcc.py → tcc_open.py}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""TCC (Toutiao Config Center) API client."""
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from typing import Any
|
|
@@ -24,11 +24,14 @@ class TCCError(Exception):
|
|
|
24
24
|
class TCCClient:
|
|
25
25
|
"""Client for TCC OpenAPI."""
|
|
26
26
|
|
|
27
|
-
def __init__(self, svc_account: str, svc_secret: str,
|
|
27
|
+
def __init__(self, svc_account: str, svc_secret: str, endpoint: str):
|
|
28
28
|
self.svc_account = svc_account
|
|
29
|
-
self.
|
|
30
|
-
self.
|
|
31
|
-
|
|
29
|
+
self.svc_secret = svc_secret
|
|
30
|
+
self.endpoint = endpoint
|
|
31
|
+
|
|
32
|
+
def _build_headers(self) -> dict[str, str]:
|
|
33
|
+
return {
|
|
34
|
+
"Authorization": f"Bearer {self.svc_secret}",
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
def publish_config(
|
|
@@ -50,7 +53,7 @@ class TCCClient:
|
|
|
50
53
|
Raises:
|
|
51
54
|
TCCError: If API call fails
|
|
52
55
|
"""
|
|
53
|
-
url = f"{self.
|
|
56
|
+
url = f"{self.endpoint}/api/v1/tcc_v3_openapi/bcc/open/config/update"
|
|
54
57
|
|
|
55
58
|
payload = {
|
|
56
59
|
"ns_name": ns_name,
|
|
@@ -67,7 +70,7 @@ class TCCClient:
|
|
|
67
70
|
|
|
68
71
|
try:
|
|
69
72
|
# print(f"writing tcc config, payload: {payload}")
|
|
70
|
-
response = requests.post(url, headers=self.
|
|
73
|
+
response = requests.post(url, headers=self._build_headers(), json=payload, timeout=30)
|
|
71
74
|
if response.status_code != 200:
|
|
72
75
|
raise TCCError(f"TCC API request failed with status {response.status_code}\n" f"Response: {response.text}")
|
|
73
76
|
|
|
@@ -108,7 +111,7 @@ class TCCClient:
|
|
|
108
111
|
Raises:
|
|
109
112
|
TCCError: If API call fails
|
|
110
113
|
"""
|
|
111
|
-
url = f"{self.
|
|
114
|
+
url = f"{self.endpoint}/api/v1/tcc_v3_openapi/bcc/open/config/list"
|
|
112
115
|
|
|
113
116
|
params = {
|
|
114
117
|
"ns_name": ns_name,
|
|
@@ -120,7 +123,7 @@ class TCCClient:
|
|
|
120
123
|
}
|
|
121
124
|
|
|
122
125
|
try:
|
|
123
|
-
response = requests.post(url, headers=self.
|
|
126
|
+
response = requests.post(url, headers=self._build_headers(), params=params, timeout=30)
|
|
124
127
|
response.raise_for_status()
|
|
125
128
|
result = response.json()
|
|
126
129
|
|
|
@@ -191,8 +194,8 @@ class TCCClient:
|
|
|
191
194
|
self,
|
|
192
195
|
ns_name: str,
|
|
193
196
|
region: str,
|
|
194
|
-
conf_name: str,
|
|
195
197
|
dir: str,
|
|
198
|
+
conf_name: str,
|
|
196
199
|
) -> dict[str, Any]:
|
|
197
200
|
"""
|
|
198
201
|
Get TCC configuration.
|
|
@@ -209,7 +212,7 @@ class TCCClient:
|
|
|
209
212
|
Raises:
|
|
210
213
|
TCCError: If API call fails
|
|
211
214
|
"""
|
|
212
|
-
url = f"{self.
|
|
215
|
+
url = f"{self.endpoint}/api/v1/tcc_v3_openapi/bcc/open/config/get"
|
|
213
216
|
|
|
214
217
|
params = {
|
|
215
218
|
"ns_name": ns_name,
|
|
@@ -219,7 +222,7 @@ class TCCClient:
|
|
|
219
222
|
}
|
|
220
223
|
|
|
221
224
|
try:
|
|
222
|
-
response = requests.get(url, headers=self.
|
|
225
|
+
response = requests.get(url, headers=self._build_headers(), params=params, timeout=30)
|
|
223
226
|
response.raise_for_status()
|
|
224
227
|
result = response.json()
|
|
225
228
|
|
dts_dance-0.1.9.dist-info/RECORD
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
dtsdance/__init__.py,sha256=Yl_jEZ5weYfcrklnDvwB4wSgCOvMBLRRgWx0gHs3qfM,49
|
|
2
|
-
dtsdance/bytecloud.py,sha256=joixJEQxX9k3VqFDNrccTVSI2WVdxdZ1JyuWFVF9zjo,4998
|
|
3
|
-
dtsdance/dflow.py,sha256=gow-zCX5lbQqqW2MRWvWynej7pZ_TQ5NmOb__j2aQT4,5497
|
|
4
|
-
dtsdance/dsyncer.py,sha256=1OateZLGekYP_EF9_oAHqsj4sdQm6ahIoRllEgT9Y3I,11142
|
|
5
|
-
dtsdance/feishu_base.py,sha256=2j4ZM4PFqJ-9EhC6DQ1OmAg--3VBGZyyRuxyjL0j6OU,3733
|
|
6
|
-
dtsdance/feishu_table.py,sha256=ZUeoKrM4nmm5hFhc3vWOVYeLP390orm-284Od92G4iQ,8424
|
|
7
|
-
dtsdance/metrics_fe.py,sha256=uPdbjGaaYBOv-rK7lKUx6aVT5Sj6ZkW9m06NyqMrPf0,18990
|
|
8
|
-
dtsdance/s3.py,sha256=Bh-cwLksfO5PewNtIzE_Md3rRLDLI1DUVoOD7Pou5T8,1294
|
|
9
|
-
dtsdance/spacex.py,sha256=wgbuwDTLXopJnLn2puX-9MfeVXvi_nQ3C9uN8McoJf4,2113
|
|
10
|
-
dtsdance/tcc.py,sha256=M_0cOVYyvUgjnC1uKWdz3YgRplafQUqX39gX_YEg8eE,6563
|
|
11
|
-
dts_dance-0.1.9.dist-info/METADATA,sha256=IbvejpKLh-EM12cf9XtlwCVkUbD5jjvRc_Pz7PA_5b8,793
|
|
12
|
-
dts_dance-0.1.9.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
13
|
-
dts_dance-0.1.9.dist-info/RECORD,,
|
|
File without changes
|