Skip to content

Commit b2c6802

Browse files
ymakedaqiSecloud
authored andcommitted
feat(dbm-services): 升级单据忽略子版本的校验 #14467
1 parent 0ed5a60 commit b2c6802

File tree

5 files changed

+201
-40
lines changed

5 files changed

+201
-40
lines changed

dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_proxy_upgrade.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,29 @@ def upgrade_mysql_proxy_flow(self):
8686
for proxy_instance in proxies:
8787
proxy_ips.append(proxy_instance.machine.ip)
8888
proxy_ips = list(set(proxy_ips))
89+
90+
# 获取目标版本
91+
target_version = get_sub_version_by_pkg_name(proxy_pkg.name)
92+
93+
# 筛选需要升级的 proxy IP(跳过所有 proxy 版本都等于目标版本的机器)
94+
upgrade_proxy_ips = []
95+
for proxy_ip in proxy_ips:
96+
ip_proxies = ProxyInstance.objects.filter(cluster__in=clusters, machine__ip=proxy_ip)
97+
all_version_match = all(p.version == target_version for p in ip_proxies)
98+
if all_version_match:
99+
logger.info(_("机器 {} 上所有 proxy 版本已是目标版本 {}, 跳过升级").format(proxy_ip, target_version))
100+
else:
101+
upgrade_proxy_ips.append(proxy_ip)
102+
103+
# 如果没有需要升级的机器,跳过整个集群
104+
if not upgrade_proxy_ips:
105+
logger.info(
106+
_("集群 {} 所有 proxy 已是目标版本 {}, 跳过升级").format(
107+
",".join([c.immute_domain for c in clusters]), target_version
108+
)
109+
)
110+
continue
111+
89112
# 切换前做预检测
90113
check_db_connect_sub_flow_list = []
91114
for cluster_id in cluster_ids:
@@ -114,27 +137,33 @@ def upgrade_mysql_proxy_flow(self):
114137
kwargs=asdict(
115138
DownloadMediaKwargs(
116139
bk_cloud_id=bk_cloud_id,
117-
exec_ip=proxy_ips,
140+
exec_ip=upgrade_proxy_ips,
118141
file_list=GetFileList(db_type=DBType.MySQL).mysql_proxy_upgrade_package(pkg_id=pkg_id),
119142
)
120143
),
121144
)
122-
for index, proxy_ip in enumerate(proxy_ips):
145+
for index, proxy_ip in enumerate(upgrade_proxy_ips):
123146
sub_pipeline.add_sub_pipeline(
124147
sub_flow=self.upgrade_mysql_proxy_subflow(
125148
bk_cloud_id=bk_cloud_id,
126149
ip=proxy_ip,
127150
pkg_id=pkg_id,
128-
proxy_version=get_sub_version_by_pkg_name(proxy_pkg.name),
151+
proxy_version=target_version,
129152
cluster_ids=cluster_ids,
130153
force_upgrade=True,
131154
)
132155
)
133156
# 最后一个节点无需再确认
134-
if index < len(proxy_ips) - 1:
157+
if index < len(upgrade_proxy_ips) - 1:
135158
sub_pipeline.add_act(act_name=_("人工确认"), act_component_code=PauseComponent.code, kwargs={})
136159

137160
sub_pipelines.append(sub_pipeline.build_sub_process(sub_name=sub_process_name))
161+
162+
# 如果所有集群都跳过了升级,则不执行流水线
163+
if not sub_pipelines:
164+
logger.info(_("所有集群的 proxy 都已是目标版本, 无需升级"))
165+
return
166+
138167
proxy_upgrade_pipeline.add_parallel_sub_pipeline(sub_flow_list=sub_pipelines)
139168
proxy_upgrade_pipeline.run_pipeline()
140169
return

dbm-ui/backend/flow/engine/bamboo/scene/mysql/validate/mysql_proxy_upgrade_validator.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,23 +112,29 @@ def run_check_for_info(self, info: dict, index: int) -> list:
112112
# 检查proxy版本升级合法性
113113
try:
114114
new_proxy_version_num = proxy_version_parse(package.name)
115-
proxies = ProxyInstance.objects.filter(cluster__in=clusters)
115+
proxies = ProxyInstance.objects.filter(cluster__in=clusters).prefetch_related("cluster")
116116

117117
if len(proxies) <= 0:
118118
error_msg = _("第{}行根据cluster_ids {}无法找到对应的proxy实例").format(index + 1, cluster_ids)
119119
error_msgs.append(error_msg)
120120
return error_msgs
121121

122122
for proxy_instance in proxies:
123+
cluster = proxy_instance.cluster.first()
124+
if not cluster:
125+
error_msg = _("第{}行proxy实例 {} 未关联任何集群").format(index + 1, proxy_instance.ip_port)
126+
error_msgs.append(error_msg)
127+
continue
128+
123129
current_version = proxy_version_parse(proxy_instance.version)
124-
if current_version >= new_proxy_version_num:
130+
if current_version > new_proxy_version_num:
125131
logger.error(
126-
_("集群 {} 的proxy实例 {} 当前版本 {} 大于等于升级版本 {}").format(
127-
proxy_instance.cluster.id, proxy_instance.ip_port, current_version, new_proxy_version_num
132+
_("集群 {} 的proxy实例 {} 当前版本 {} 大于升级版本 {}").format(
133+
cluster.id, proxy_instance.ip_port, current_version, new_proxy_version_num
128134
)
129135
)
130136
error_msg = _("第{}行集群 {} 的proxy实例 {} 待升级版本大于等于当前版本,请确认升级的版本").format(
131-
index + 1, proxy_instance.cluster.id, proxy_instance.ip_port
137+
index + 1, cluster.id, proxy_instance.ip_port
132138
)
133139
error_msgs.append(error_msg)
134140

dbm-ui/backend/flow/engine/bamboo/scene/spider/upgrade/upgrade_spider_node.py

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,7 @@
4141
add_spider_upgrade_check_act,
4242
build_spider_upgrade_subflow,
4343
)
44-
from .upgrade_utils import (
45-
check_cross_major_version_upgrade,
46-
check_spider_node_count_compatibility,
47-
check_spider_upgrade_version_compatibility,
48-
get_spider_master_instances,
49-
get_spider_upgrade_instances,
50-
)
44+
from .upgrade_utils import check_cross_major_version_upgrade, get_spider_master_instances, get_spider_upgrade_instances
5145

5246
logger = logging.getLogger("flow")
5347

@@ -119,7 +113,7 @@ def run(self):
119113
执行spider升级流程的主入口方法
120114
121115
执行流程:
122-
1. 执行前置检查(__pre_check):验证升级版本和节点数量
116+
1. 前置校验已由 TenDBClusterSpiderUpgradeValidator 完成(包括版本兼容性和节点数量检查)
123117
2. 根据upgrade_local参数选择升级模式:
124118
- True: 执行本地升级(local_upgrade)
125119
- False: 执行迁移升级(migrate_upgrade)
@@ -128,9 +122,6 @@ def run(self):
128122
- 本地升级:在现有机器上直接升级spider版本,适用于版本兼容性好的场景
129123
- 迁移升级:通过新增机器替换旧机器的方式进行升级,适用于需要保证服务连续性的场景
130124
"""
131-
# 执行前置检查:验证升级版本和节点数量
132-
self.__pre_check()
133-
134125
# 根据升级模式选择执行路径
135126
if self.upgrade_local:
136127
# 本地升级:在现有机器上直接升级版本
@@ -139,17 +130,6 @@ def run(self):
139130
# 迁移升级:通过新增机器替换旧机器进行升级
140131
self.migrate_upgrade()
141132

142-
# spider_ins.tendbclusterspiderext.spider_role
143-
def __pre_check(self):
144-
"""
145-
检查升级版本和源版本
146-
"""
147-
# 检查版本兼容性
148-
check_spider_upgrade_version_compatibility(self.data)
149-
150-
# 检查节点数量兼容性
151-
check_spider_node_count_compatibility(self.data)
152-
153133
def local_upgrade(self):
154134
"""
155135
spider 本地升级场景

dbm-ui/backend/flow/engine/bamboo/scene/spider/upgrade/upgrade_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -492,9 +492,9 @@ def check_spider_upgrade_version_compatibility(data: Dict) -> None:
492492
for spider_ins in spiders:
493493
current_version = tspider_version_parse(spider_ins.version)
494494
current_versions.add(spider_ins.version)
495-
if current_version >= new_spider_version_num:
495+
if current_version > new_spider_version_num:
496496
logger.error(_("待升级版本 {} 需要大于当前版本 {}").format(new_spider_version_num, current_version))
497-
raise DBMetaException(message=_("待升级版本大于等于新版本,请确认升级的版本"))
497+
raise DBMetaException(message=_("待升级版本大于新版本,请确认升级的版本"))
498498

499499

500500
def check_spider_node_count_compatibility(data: Dict) -> None:

dbm-ui/backend/flow/engine/bamboo/scene/spider/validate/spider_upgrade_validate.py

Lines changed: 153 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,15 @@
1010
from django.utils.translation import gettext as _
1111

1212
from backend.db_meta.enums import TenDBClusterSpiderRole
13-
from backend.db_meta.models import Cluster, Machine
14-
from backend.flow.engine.bamboo.scene.spider.validate.exception import SpiderRoleFailedException
13+
from backend.db_meta.models import Cluster, Machine, ProxyInstance
14+
from backend.db_package.models import Package
15+
from backend.flow.consts import MediumEnum
16+
from backend.flow.engine.bamboo.scene.spider.validate.exception import (
17+
SpiderRoleFailedException,
18+
UpgradeVersionFailedException,
19+
)
1520
from backend.flow.engine.validate.mysql_base_validate import MysqlBaseValidator
21+
from backend.flow.utils.mysql.mysql_version_parse import spider_cross_major_version, tspider_version_parse
1622

1723

1824
class TenDBClusterSpiderUpgradeValidator(MysqlBaseValidator):
@@ -119,27 +125,167 @@ def pre_check_spider_spec_consistency(self):
119125

120126
return error_msgs
121127

128+
def pre_check_spider_upgrade_version_compatibility(self):
129+
"""
130+
检查spider升级版本兼容性
131+
132+
校验逻辑:
133+
1. 遍历所有待升级的集群信息
134+
2. 获取目标版本包信息和当前集群所有spider实例版本
135+
3. 确保目标版本大于所有当前版本
136+
137+
返回:
138+
- list: 错误信息列表,如果没有错误则返回空列表
139+
"""
140+
error_msgs = []
141+
142+
for info in self.data["infos"]:
143+
pkg_id = info["pkg_id"]
144+
cluster_id = info["cluster_id"]
145+
spider_pkg = Package.objects.get(id=pkg_id, pkg_type=MediumEnum.Spider)
146+
new_spider_version_num = tspider_version_parse(spider_pkg.name)
147+
cluster = Cluster.objects.get(id=cluster_id)
148+
spiders = ProxyInstance.objects.filter(cluster=cluster)
149+
150+
for spider_ins in spiders:
151+
current_version = tspider_version_parse(spider_ins.version)
152+
if current_version > new_spider_version_num:
153+
error_msg = _("集群 {} 待升级版本 {} 需要大于当前版本 {}").format(
154+
cluster.immute_domain, new_spider_version_num, current_version
155+
)
156+
error_msgs.append(error_msg)
157+
break # 一个集群发现一个不兼容版本就跳出
158+
159+
return error_msgs
160+
161+
def pre_check_spider_node_count_compatibility(self):
162+
"""
163+
检查spider节点数量兼容性(仅迁移升级场景)
164+
165+
校验逻辑:
166+
1. 仅在非本地升级(迁移升级)场景下进行检查
167+
2. 检查新传入的spider master IP数量与现有节点数量是否一致
168+
3. 检查新传入的spider slave IP数量与现有节点数量是否一致
169+
170+
返回:
171+
- list: 错误信息列表,如果没有错误则返回空列表
172+
"""
173+
error_msgs = []
174+
175+
# 仅在迁移升级场景下进行检查
176+
if self.data.get("upgrade_local", False):
177+
return error_msgs
178+
179+
for info in self.data["infos"]:
180+
cluster_id = info["cluster_id"]
181+
cluster = Cluster.objects.get(id=cluster_id)
182+
183+
spider_master_ip_list = info.get("spider_master_ip_list", [])
184+
spider_slave_ip_list = info.get("spider_slave_ip_list", [])
185+
186+
master_spiders_count = cluster.proxyinstance_set.filter(
187+
tendbclusterspiderext__spider_role=TenDBClusterSpiderRole.SPIDER_MASTER
188+
).count()
189+
if master_spiders_count != len(spider_master_ip_list):
190+
error_msg = _("集群 {} 待升级spiderMaster节点数({})与传入ip节点数({})不一致,请确认").format(
191+
cluster.immute_domain, master_spiders_count, len(spider_master_ip_list)
192+
)
193+
error_msgs.append(error_msg)
194+
195+
slave_spiders_count = cluster.proxyinstance_set.filter(
196+
tendbclusterspiderext__spider_role=TenDBClusterSpiderRole.SPIDER_SLAVE
197+
).count()
198+
if slave_spiders_count > 0 and len(spider_slave_ip_list) != slave_spiders_count:
199+
error_msg = _("集群 {} 待升级spiderSlave节点数({})与传入ip节点数({})不一致,请确认").format(
200+
cluster.immute_domain, slave_spiders_count, len(spider_slave_ip_list)
201+
)
202+
error_msgs.append(error_msg)
203+
204+
return error_msgs
205+
206+
def pre_check_local_upgrade_cross_major_version(self):
207+
"""
208+
检查本地升级时spider实例之间是否存在跨主版本
209+
210+
校验逻辑:
211+
1. 仅在本地升级场景(upgrade_local=True)下进行检查
212+
2. 遍历所有待升级的集群信息
213+
3. 获取每个集群的所有spider实例版本
214+
4. 比较最大版本和最小版本是否跨主版本
215+
5. 如果发现跨主版本,记录错误信息
216+
217+
返回:
218+
- list: 错误信息列表,如果没有错误则返回空列表
219+
"""
220+
error_msgs = []
221+
222+
# 仅在本地升级场景下进行检查
223+
if not self.data.get("upgrade_local", False):
224+
return error_msgs
225+
226+
for info in self.data["infos"]:
227+
cluster_id = info["cluster_id"]
228+
cluster = Cluster.objects.get(id=cluster_id)
229+
spiders = ProxyInstance.objects.filter(cluster=cluster)
230+
231+
if not spiders.exists():
232+
continue
233+
234+
# 收集所有spider实例的版本号
235+
version_nums = [tspider_version_parse(spider.version) for spider in spiders]
236+
237+
# 比较最大版本和最小版本是否跨主版本
238+
max_version = max(version_nums)
239+
min_version = min(version_nums)
240+
241+
if spider_cross_major_version(max_version, min_version):
242+
all_versions = set(spider.version for spider in spiders)
243+
error_msg = _("集群 {} 本地升级不允许spider实例之间存在跨主版本,当前存在版本: {}").format(
244+
cluster.immute_domain, ", ".join(all_versions)
245+
)
246+
error_msgs.append(error_msg)
247+
248+
return error_msgs
249+
122250
def __call__(self):
123251
"""
124252
发起校验,实例函数化
125253
126254
执行流程:
127-
1. 调用pre_check_spider_master_spec_consistency方法检查规格一致性
128-
2. 如果发现错误,抛出SpiderRoleFailedException异常
129-
3. 如果没有错误,返回None表示校验通过
255+
1. 调用pre_check_spider_spec_consistency方法检查规格一致性
256+
2. 调用pre_check_spider_upgrade_version_compatibility方法检查版本兼容性
257+
3. 调用pre_check_spider_node_count_compatibility方法检查节点数量兼容性
258+
4. 如果发现错误,抛出对应的异常
259+
5. 如果没有错误,返回None表示校验通过
130260
131261
异常处理:
132262
- 当发现规格不一致时,抛出SpiderRoleFailedException
133-
- 异常消息包含所有发现的规格不一致问题
263+
- 当发现版本不兼容时,抛出UpgradeVersionFailedException
264+
- 异常消息包含所有发现的问题
134265
135266
返回:
136267
- None: 校验通过
137-
- 异常: 校验失败时抛出SpiderRoleFailedException
268+
- 异常: 校验失败时抛出对应异常
138269
"""
139270
# 检查spider机器规格一致性(包括spider_master和spider_slave)
140271
error_msgs = self.pre_check_spider_spec_consistency()
141272
if error_msgs:
142273
# 将所有错误信息合并为一个字符串,并抛出异常
143274
raise SpiderRoleFailedException("\n".join(error_msgs))
144275

276+
# 检查spider升级版本兼容性
277+
error_msgs = self.pre_check_spider_upgrade_version_compatibility()
278+
if error_msgs:
279+
raise UpgradeVersionFailedException("\n".join(error_msgs))
280+
281+
# 检查spider节点数量兼容性(仅迁移升级场景)
282+
error_msgs = self.pre_check_spider_node_count_compatibility()
283+
if error_msgs:
284+
raise UpgradeVersionFailedException("\n".join(error_msgs))
285+
286+
# 检查本地升级时是否跨主版本
287+
error_msgs = self.pre_check_local_upgrade_cross_major_version()
288+
if error_msgs:
289+
raise UpgradeVersionFailedException("\n".join(error_msgs))
290+
145291
return None

0 commit comments

Comments
 (0)