Skip to content

Commit

Permalink
feat: 安装预设插件锁定版本 (closed #2482)
Browse files Browse the repository at this point in the history
� Conflicts:
�	apps/node_man/models.py
  • Loading branch information
jpyoung3 authored and wyyalt committed Dec 27, 2024
1 parent 81b1d3c commit afa880c
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 74 deletions.
12 changes: 8 additions & 4 deletions apps/backend/components/collections/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ def get_package_by_process_status(
"""通过进程状态得到插件包对象"""
host = self.get_host_by_process_status(process_status, common_data)
policy_step_adapter = common_data.policy_step_adapter
package = policy_step_adapter.get_matching_package_obj(host.os_type, host.cpu_arch)
package = policy_step_adapter.get_matching_package_obj(host.os_type, host.cpu_arch, host.bk_biz_id)
return package

def get_plugin_root_by_process_status(
Expand Down Expand Up @@ -280,11 +280,12 @@ def _execute(self, data, parent_data, common_data: PluginCommonData):
# target_host_objs 的长度通常为1或2,此处也不必担心时间复杂度问题
# 指定 target_host 主要用于远程采集的场景,常见于第三方插件,如拨测
for host in target_host_objs:
bk_biz_id = host.bk_biz_id
bk_host_id = host.bk_host_id
os_type = host.os_type.lower()
cpu_arch = host.cpu_arch
group_id = create_group_id(subscription, subscription_instance.instance_info)
package = self.get_package(subscription_instance, policy_step_adapter, os_type, cpu_arch)
package = self.get_package(subscription_instance, policy_step_adapter, os_type, cpu_arch, bk_biz_id)
ap_config = self.get_ap_config(ap_id_obj_map, host)
setup_path, pid_path, log_path, data_path = self.get_plugins_paths(
package, plugin_name, ap_config, group_id, subscription
Expand Down Expand Up @@ -340,10 +341,11 @@ def get_package(
policy_step_adapter: PolicyStepAdapter,
os_type: str,
cpu_arch: str,
bk_biz_id: int,
) -> models.Packages:
"""获取插件包对象"""
try:
return policy_step_adapter.get_matching_package_obj(os_type, cpu_arch)
return policy_step_adapter.get_matching_package_obj(os_type, cpu_arch, bk_biz_id)
except errors.PackageNotExists as error:
# 插件包不支持或不存在时,记录异常信息,此实例不参与后续流程
self.move_insts_to_failed([subscription_instance.id], str(error))
Expand Down Expand Up @@ -979,7 +981,9 @@ def _execute(self, data, parent_data, common_data: PluginCommonData):

# 根据配置模板和上下文变量渲染配置文件
rendered_configs = render_config_files_by_config_templates(
policy_step_adapter.get_matching_config_tmpl_objs(target_host.os_type, target_host.cpu_arch),
policy_step_adapter.get_matching_config_tmpl_objs(
target_host.os_type, target_host.cpu_arch, package, subscription_step.config
),
{"group_id": process_status.group_id},
context,
package_obj=package,
Expand Down
117 changes: 104 additions & 13 deletions apps/backend/subscription/steps/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from django.db.models import Max, Subquery, Value
from django.utils.translation import ugettext as _
from packaging import version
from rest_framework import exceptions, serializers

from apps.backend.subscription import errors
Expand Down Expand Up @@ -444,27 +445,117 @@ def get_matching_step_params(self, os_type: str = None, cpu_arch: str = None, os
return self.os_key_params_map.get(os_key)
return self.os_key_params_map.get(self.get_os_key(os_type, cpu_arch), {})

def get_matching_package_obj(self, os_type: str, cpu_arch: str) -> models.Packages:
def get_matching_package_obj(self, os_type: str, cpu_arch: str, bk_biz_id: int) -> models.Packages:
try:
package = self.os_key_pkg_map[self.get_os_key(os_type, cpu_arch)]
except KeyError:
msg = _("插件 [{name}] 不支持 系统:{os_type}-架构:{cpu_arch}-版本:{plugin_version}").format(
name=self.plugin_name,
os_type=os_type,
cpu_arch=cpu_arch,
plugin_version=self.get_matching_package_dict(os_type, cpu_arch)["version"],
# 如果不存在某个系统架构的版本,则获取最大id的版本
package = (
models.Packages.objects.filter(project=self.plugin_name, os=os_type, cpu_arch=cpu_arch)
.order_by("-id")
.first()
)
raise errors.PackageNotExists(msg)
else:
if not package.is_ready:
msg = _("插件 [{name}] 系统:{os_type}-架构:{cpu_arch}-版本:{plugin_version} 未启用").format(
if not package:
msg = _("插件 [{name}] 不支持 系统:{os_type}-架构:{cpu_arch}-版本:{plugin_version}").format(
name=self.plugin_name,
os_type=os_type,
cpu_arch=cpu_arch,
plugin_version=self.get_matching_package_dict(os_type, cpu_arch)["version"],
)
raise errors.PluginValidationError(msg)
return package
raise errors.PackageNotExists(msg)

def get_matching_config_tmpl_objs(self, os_type: str, cpu_arch: str) -> List[models.PluginConfigTemplate]:
if not package.is_ready:
msg = _("插件 [{name}] 系统:{os_type}-架构:{cpu_arch}-版本:{plugin_version} 未启用").format(
name=self.plugin_name,
os_type=os_type,
cpu_arch=cpu_arch,
plugin_version=self.get_matching_package_dict(os_type, cpu_arch)["version"],
)
raise errors.PluginValidationError(msg)

if len(self.selected_pkg_infos) > 1:
package = self.check_biz_version(package, bk_biz_id)
return package

def get_matching_config_tmpl_objs(
self, os_type: str, cpu_arch: str, package: models.Packages = None, config: Dict = None
) -> List[models.PluginConfigTemplate]:
"""如果 package 是重新获取的(包括业务锁定版本和tag不存在的版本两种情况),则重新从数据库中获取配置模板"""
if not self.is_pkg_in_selected_pkg(package, self.selected_pkg_infos):
plugin_config_templates = []
for config_template in config["config_templates"]:
config_tmpl = (
models.PluginConfigTemplate.objects.filter(
name=config_template["name"],
plugin_name=package.project,
plugin_version=package.version,
is_main=Value(1 if config_template["is_main"] else 0),
)
.order_by("-id")
.first()
)
plugin_config_templates.append(config_tmpl)
return plugin_config_templates
return self.config_tmpl_obj_gby_os_key.get(self.get_os_key(os_type, cpu_arch), [])

def check_biz_version(self, package: models.Packages, bk_biz_id: int):
"""如果设定了业务最大版本,则判断当前版本是否大于业务设定的最大版本"""
plugin_version_config = self.plugin_version_config()
if str(bk_biz_id) in plugin_version_config:
biz_version_config = plugin_version_config[str(bk_biz_id)]
biz_version = next(
(
biz_plugin_version
for biz_plugin_name, biz_plugin_version in biz_version_config.items()
if package.project == biz_plugin_name
),
None,
)
if biz_version:
version_str = getattr(package, "version", "")
tag_name__obj_map: Dict[str, Tag] = PluginTargetHelper.get_tag_name__obj_map(
target_id=self.plugin_desc.id,
)
if version_str in tag_name__obj_map:
version_str = tag_name__obj_map[version_str].target_version
if version.Version(version_str) > version.Version(biz_version):
package = self.get_biz_max_package(package.project, package.os, package.cpu_arch, biz_version)
return package

@staticmethod
def get_biz_max_package(plugin_name: str, os_type: str, cpu_arch: str, biz_version: str):
"""获取业务锁定版本的插件包"""
packages = models.Packages.objects.filter(project=plugin_name, os=os_type, cpu_arch=cpu_arch)
lte_biz_version_packages = []
for package in packages:
try:
pkg_version = version.Version(package.version)
if pkg_version <= version.Version(biz_version):
lte_biz_version_packages.append(package)
except version.InvalidVersion:
continue
max_version_package = None
if lte_biz_version_packages:
max_version_package = max(lte_biz_version_packages, key=lambda pkg: version.Version(pkg.version))
return max_version_package

@staticmethod
def plugin_version_config():
"""业务锁定版本配置"""
plugin_version_config: Dict[str, Dict[str, str]] = models.GlobalSettings.get_config(
models.GlobalSettings.KeyEnum.PLUGIN_VERSION_CONFIG.value, default={}
)
return plugin_version_config

@staticmethod
def is_pkg_in_selected_pkg(package: models.Packages, selected_pkg_infos: List[Dict]) -> bool:
for pkg_info in selected_pkg_infos:
if (
package.project == pkg_info["project"]
and package.id == pkg_info["id"]
and package.version == pkg_info["version"]
and package.os == pkg_info["os"]
and package.cpu_arch == pkg_info["cpu_arch"]
):
return True
return False
74 changes: 17 additions & 57 deletions apps/backend/tests/plugin/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,45 +149,19 @@
"type": "object",
"required": True,
"properties": {
"token": {
"title": "token",
"type": "string",
"required": True
},
"logVerbosity": {
"title": "logVerbosity",
"type": "number",
"required": False,
"default": 5
},
"tempDir": {
"title": "tempDir",
"type": "string",
"required": False
},
"uid": {
"title": "uid",
"type": "string",
"required": False
},
"token": {"title": "token", "type": "string", "required": True},
"logVerbosity": {"title": "logVerbosity", "type": "number", "required": False, "default": 5},
"tempDir": {"title": "tempDir", "type": "string", "required": False},
"uid": {"title": "uid", "type": "string", "required": False},
"labels": {
"title": "labels",
"type": "array",
"items": {
"title": "label",
"type": "object",
"required": False,
"properties": {
"key": {
"title": "key",
"type": "string"
},
"value": {
"title": "value",
"type": "string"
}
}
}
"properties": {"key": {"title": "键", "type": "string"}, "value": {"title": "值", "type": "string"}},
},
},
"apps": {
"title": "apps",
Expand All @@ -196,16 +170,8 @@
"title": "named_label",
"type": "object",
"properties": {
"name": {
"title": "name",
"type": "string",
"required": True
},
"uid": {
"title": "uid",
"type": "string",
"required": False
},
"name": {"title": "name", "type": "string", "required": True},
"uid": {"title": "uid", "type": "string", "required": False},
"labels": {
"title": "labels",
"type": "array",
Expand All @@ -215,21 +181,15 @@
"type": "object",
"required": False,
"properties": {
"key": {
"title": "key",
"type": "string"
},
"value": {
"title": "value",
"type": "string"
}
}
}
}
}
}
}
}
"key": {"title": "键", "type": "string"},
"value": {"title": "值", "type": "string"},
},
},
},
},
},
},
},
}

# 插件名称
Expand Down
2 changes: 2 additions & 0 deletions apps/node_man/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ class KeyEnum(Enum):
CMDB_INTERNAL_CLOUD_IDS = "CMDB_INTERNAL_CLOUD_IDS"
# GSE查询进程状态信息分片大小
QUERY_PROC_STATUS_HOST_LENS = "QUERY_PROC_STATUS_HOST_LENS"
# 业务最大插件版本
PLUGIN_VERSION_CONFIG = "PLUGIN_VERSION_CONFIG"

key = models.CharField(_("键"), max_length=255, db_index=True, primary_key=True)
v_json = JSONField(_("值"))
Expand Down

0 comments on commit afa880c

Please sign in to comment.