Skip to content

Commit 8fe2b7c

Browse files
committed
feat: 卸载agent时停止插件并清理agent安装及运行过程中产生的目录(closed #2490)
1 parent f14f01b commit 8fe2b7c

File tree

16 files changed

+469
-12
lines changed

16 files changed

+469
-12
lines changed

apps/backend/agent/manager.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,11 @@ def install_other_agent(cls, extra_agent_version: str, node_type: str = NodeType
297297
act.component.inputs.extra_agent_version = Var(type=Var.PLAIN, value=extra_agent_version)
298298
act.component.inputs.node_type = Var(type=Var.PLAIN, value=node_type)
299299
return act
300+
301+
@classmethod
302+
def stop_plugins(cls):
303+
"""停止插件"""
304+
act = AgentServiceActivity(
305+
component_code=components.StopPluginsComponent.code, name=components.StopPluginsComponent.name
306+
)
307+
return act

apps/backend/components/collections/agent_new/components.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from .render_and_push_gse_config import RenderAndPushGseConfigService
3737
from .restart import RestartService
3838
from .run_upgrade_command import RunUpgradeCommandService
39+
from .stop_plugins import StopPluginsService
3940
from .unbind_host_agent import UnBindHostAgentService
4041
from .update_install_info import UpdateInstallInfoService
4142
from .update_process_status import UpdateProcessStatusService
@@ -221,3 +222,9 @@ class InstallOtherAgentComponent(Component):
221222
name = _("安装额外Agent")
222223
code = "install_other_agent"
223224
bound_service = InstallOtherAgentService
225+
226+
227+
class StopPluginsComponent(Component):
228+
name = _("停止插件")
229+
code = "stop_plugins"
230+
bound_service = StopPluginsService
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-节点管理(BlueKing-BK-NODEMAN) available.
4+
Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. All rights reserved.
5+
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at https://opensource.org/licenses/MIT
7+
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
8+
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
9+
specific language governing permissions and limitations under the License.
10+
"""
11+
12+
from collections import defaultdict
13+
from typing import List
14+
15+
from django.conf import settings
16+
17+
from apps.core.concurrent.retry import RetryHandler
18+
from apps.node_man import constants, models
19+
from apps.utils.batch_request import request_multi_thread
20+
from common.api import NodeApi
21+
from common.api.exception import DataAPIException
22+
23+
from ..base import CommonData
24+
from ..subsubscription import SubSubscriptionBaseService
25+
from .base import AgentBaseService
26+
27+
28+
class StopPluginsService(SubSubscriptionBaseService, AgentBaseService):
29+
@staticmethod
30+
@RetryHandler(interval=1, retry_times=1, exception_types=[DataAPIException])
31+
def call_create_subscription_api(params):
32+
return NodeApi.create_subscription(params)
33+
34+
@classmethod
35+
def create_subscriptions(cls, common_data: CommonData) -> List[int]:
36+
37+
host_ids_group_by_os = defaultdict(list)
38+
for host in common_data.host_id_obj_map.values():
39+
host_ids_group_by_os[host.os_type.lower()].append(host.bk_host_id)
40+
41+
installed_running_plugin_names = models.ProcessStatus.objects.filter(
42+
status=constants.ProcStateType.RUNNING,
43+
bk_host_id__in=common_data.bk_host_ids,
44+
proc_type=constants.ProcType.PLUGIN,
45+
).values_list("name", flat=True)
46+
47+
plugin_name__os_type_set = set(
48+
models.Packages.objects.filter(
49+
project__in=installed_running_plugin_names, os__in=host_ids_group_by_os.keys()
50+
).values_list("project", "os")
51+
)
52+
params_list = []
53+
for (plugin_name, os_type) in plugin_name__os_type_set:
54+
params_list.append(
55+
{
56+
"params": {
57+
"run_immediately": True,
58+
"category": models.Subscription.CategoryType.ONCE,
59+
"bk_username": settings.SYSTEM_USE_API_ACCOUNT,
60+
"scope": {
61+
"node_type": models.Subscription.NodeType.INSTANCE,
62+
"object_type": models.Subscription.ObjectType.HOST,
63+
"nodes": [{"bk_host_id": bk_host_id} for bk_host_id in host_ids_group_by_os[os_type]],
64+
},
65+
"steps": [
66+
{
67+
"id": plugin_name,
68+
"type": "PLUGIN",
69+
"config": {
70+
"job_type": constants.JobType.MAIN_STOP_PLUGIN,
71+
"plugin_name": plugin_name,
72+
"plugin_version": "latest",
73+
"config_templates": [
74+
{"name": "{}.conf".format(plugin_name), "version": "latest", "is_main": True}
75+
],
76+
},
77+
"params": {"context": {}},
78+
}
79+
],
80+
}
81+
}
82+
)
83+
subscription_ids = request_multi_thread(
84+
cls.call_create_subscription_api, params_list, get_data=lambda x: [x["subscription_id"]]
85+
)
86+
return subscription_ids

apps/backend/subscription/steps/agent.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,7 @@ class UninstallAgent(AgentAction):
497497
def _generate_activities(self, agent_manager: AgentManager):
498498
activities = [
499499
agent_manager.query_password(),
500+
agent_manager.stop_plugins(),
500501
agent_manager.uninstall_agent(),
501502
agent_manager.get_agent_status(expect_status=constants.ProcStateType.UNKNOWN),
502503
agent_manager.update_process_status(status=constants.ProcStateType.NOT_INSTALLED),
@@ -515,6 +516,7 @@ class UninstallProxy(AgentAction):
515516

516517
def _generate_activities(self, agent_manager: AgentManager):
517518
activities = [
519+
agent_manager.stop_plugins(),
518520
agent_manager.uninstall_proxy(),
519521
agent_manager.get_agent_status(expect_status=constants.ProcStateType.UNKNOWN, name=_("查询Proxy状态")),
520522
agent_manager.update_process_status(status=constants.ProcStateType.NOT_INSTALLED),
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-节点管理(BlueKing-BK-NODEMAN) available.
4+
Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. All rights reserved.
5+
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at https://opensource.org/licenses/MIT
7+
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
8+
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
9+
specific language governing permissions and limitations under the License.
10+
"""
11+
from typing import List
12+
13+
import mock
14+
15+
from apps.backend.api.constants import POLLING_INTERVAL
16+
from apps.backend.components.collections.agent_new.components import (
17+
StopPluginsComponent,
18+
)
19+
from apps.mock_data import common_unit
20+
from apps.node_man import models
21+
from apps.node_man.tests.utils import NodeApi
22+
from pipeline.component_framework.test import (
23+
ComponentTestCase,
24+
ExecuteAssertion,
25+
ScheduleAssertion,
26+
)
27+
28+
from . import utils
29+
30+
31+
class StopPluginsTestCase(utils.AgentServiceBaseTestCase):
32+
CASE_NAME = "测试子订阅停止插件成功"
33+
NODEMAN_API_MOCK_PATHS = [
34+
"common.api.NodeApi",
35+
"apps.backend.components.collections.subsubscription.NodeApi",
36+
"apps.backend.components.collections.agent_new.stop_plugins.NodeApi",
37+
]
38+
SUBSCRIPTION_ID = common_unit.subscription.DEFAULT_SUBSCRIPTION_ID
39+
EXECUTE_RESULT = True
40+
IS_FINISHED = True
41+
SCHEDULE_RESULT = True
42+
EXTRA_OUTPUT = {}
43+
44+
@staticmethod
45+
def init_plugins():
46+
models.GsePluginDesc.objects.create(**common_unit.plugin.GSE_PLUGIN_DESC_MODEL_DATA)
47+
models.Packages.objects.create(**common_unit.plugin.PACKAGES_MODEL_DATA)
48+
models.ProcessStatus.objects.create(**common_unit.plugin.PROC_STATUS_MODEL_DATA)
49+
50+
def start_patch(self):
51+
class CustomNodeApi(NodeApi):
52+
@staticmethod
53+
def create_subscription(params):
54+
return {"subscription_id": self.SUBSCRIPTION_ID}
55+
56+
@staticmethod
57+
def get_subscription_task_status(params):
58+
task_results = NodeApi.get_subscription_task_status(params)
59+
for task_result in task_results:
60+
task_result["instance_info"]["host"]["bk_host_id"] = self.obj_factory.bk_host_ids[0]
61+
return task_results
62+
63+
for nodeman_api_mock_path in self.NODEMAN_API_MOCK_PATHS:
64+
mock.patch(nodeman_api_mock_path, CustomNodeApi).start()
65+
66+
def setUp(self) -> None:
67+
self.init_plugins()
68+
super().setUp()
69+
self.start_patch()
70+
71+
def fetch_succeeded_sub_inst_ids(self) -> List[int]:
72+
return self.common_inputs["subscription_instance_ids"]
73+
74+
def component_cls(self):
75+
return StopPluginsComponent
76+
77+
def cases(self):
78+
return [
79+
ComponentTestCase(
80+
name=self.CASE_NAME,
81+
inputs=self.common_inputs,
82+
parent_data={},
83+
execute_assertion=ExecuteAssertion(
84+
success=self.EXECUTE_RESULT,
85+
outputs={
86+
"subscription_ids": [self.SUBSCRIPTION_ID],
87+
"all_subscription_ids": [self.SUBSCRIPTION_ID],
88+
"succeeded_subscription_instance_ids": self.fetch_succeeded_sub_inst_ids(),
89+
},
90+
),
91+
schedule_assertion=ScheduleAssertion(
92+
success=self.SCHEDULE_RESULT,
93+
schedule_finished=self.IS_FINISHED,
94+
outputs={
95+
"subscription_ids": [self.SUBSCRIPTION_ID],
96+
"all_subscription_ids": [self.SUBSCRIPTION_ID],
97+
"succeeded_subscription_instance_ids": self.fetch_succeeded_sub_inst_ids(),
98+
**self.EXTRA_OUTPUT,
99+
},
100+
),
101+
execute_call_assertion=None,
102+
)
103+
]
104+
105+
@classmethod
106+
def tearDownClass(cls):
107+
mock.patch.stopall()
108+
super().tearDownClass()
109+
110+
111+
class StopPluginsNoSubTestCase(StopPluginsTestCase):
112+
CASE_NAME = "测试无需创建子订阅"
113+
114+
@staticmethod
115+
def init_plugins():
116+
pass
117+
118+
def cases(self):
119+
return [
120+
ComponentTestCase(
121+
name=self.CASE_NAME,
122+
inputs=self.common_inputs,
123+
parent_data={},
124+
execute_assertion=ExecuteAssertion(
125+
success=self.EXECUTE_RESULT,
126+
outputs={
127+
"subscription_ids": [],
128+
"all_subscription_ids": [],
129+
"succeeded_subscription_instance_ids": self.fetch_succeeded_sub_inst_ids(),
130+
},
131+
),
132+
schedule_assertion=[],
133+
)
134+
]
135+
136+
137+
class StopPluginsFailedTestCase(StopPluginsTestCase):
138+
CASE_NAME = "测试子订阅停止插件失败"
139+
SUBSCRIPTION_ID = common_unit.subscription.FAILED_SUBSCRIPTION_ID
140+
SCHEDULE_RESULT = False
141+
EXTRA_OUTPUT = {"succeeded_subscription_instance_ids": []}
142+
143+
144+
class StopPluginsCreateSubTaskFailedTestCase(StopPluginsTestCase):
145+
CASE_NAME = "测试子订阅任务创建失败"
146+
SUBSCRIPTION_ID = common_unit.subscription.CREATE_TASK_FAILED_SUBSCRIPTION_ID
147+
SCHEDULE_RESULT = False
148+
EXTRA_OUTPUT = {"succeeded_subscription_instance_ids": [], "subscription_ids": []}
149+
150+
151+
class StopPluginsRunningTestCase(StopPluginsTestCase):
152+
CASE_NAME = "测试子订阅停止插件运行中"
153+
SUBSCRIPTION_ID = common_unit.subscription.RUNNING_SUBSCRIPTION_ID
154+
IS_FINISHED = False
155+
EXTRA_OUTPUT = {"polling_time": POLLING_INTERVAL}

script_tools/agent_tools/agent2/setup_agent.sh

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,16 @@ remove_crontab () {
326326
fi
327327
}
328328

329+
remove_directory () {
330+
for dir in "$@"; do
331+
if [ -d "$dir" ]; then
332+
log remove_directory - "trying to remove directory [${dir}]"
333+
rm -rf "$dir"
334+
log remove_directory - "directory [${dir}] removed"
335+
fi
336+
done
337+
}
338+
329339
setup_startup_scripts () {
330340
check_rc_file
331341
local rcfile=$RC_LOCAL_FILE
@@ -480,7 +490,8 @@ remove_agent () {
480490

481491
if [[ "$REMOVE" == "TRUE" ]]; then
482492
unregister_agent_id
483-
clean_up_agent_directory
493+
remove_directory "$GSE_HOME" "$GSE_AGENT_RUN_DIR" "$GSE_AGENT_DATA_DIR" "$GSE_AGENT_LOG_DIR"
494+
484495
log remove_agent DONE "agent removed"
485496
exit 0
486497
fi
@@ -903,6 +914,7 @@ done
903914
PKG_NAME=${NAME}-${VERSION}.tgz
904915
COMPLETE_DOWNLOAD_URL="${DOWNLOAD_URL}/agent/linux/${CPU_ARCH}"
905916
GSE_AGENT_CONFIG_PATH="${AGENT_SETUP_PATH}/etc/${GSE_AGENT_CONFIG}"
917+
GSE_HOME=$(dirname ${AGENT_SETUP_PATH})
906918

907919
LOG_FILE="$TMP_DIR"/nm.${0##*/}.$TASK_ID
908920
DEBUG_LOG_FILE=${TMP_DIR}/nm.${0##*/}.${TASK_ID}.debug

script_tools/agent_tools/agent2/setup_agent.zsh

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,16 @@ remove_crontab () {
326326
fi
327327
}
328328

329+
remove_directory () {
330+
for dir in "$@"; do
331+
if [ -d "$dir" ]; then
332+
log remove_directory - "trying to remove directory [${dir}]"
333+
rm -rf "$dir"
334+
log remove_directory - "directory [${dir}] removed"
335+
fi
336+
done
337+
}
338+
329339
get_daemon_file () {
330340
DAEMON_FILE_PATH="/Library/LaunchDaemons/"
331341
DAEMON_FILE_NAME="com.tencent.$(echo ${AGENT_SETUP_PATH%*/} | tr '/' '.' | awk -F '.' '{print $(NF-1)"."$NF}').Daemon.plist"
@@ -492,7 +502,7 @@ remove_agent () {
492502

493503
if [[ "$REMOVE" == "TRUE" ]]; then
494504
unregister_agent_id
495-
clean_up_agent_directory
505+
remove_directory "$GSE_HOME" "$GSE_AGENT_RUN_DIR" "$GSE_AGENT_DATA_DIR" "$GSE_AGENT_LOG_DIR"
496506
log remove_agent DONE "agent removed"
497507
exit 0
498508
fi
@@ -850,6 +860,7 @@ check_env () {
850860
CLOUD_ID=0
851861
TMP_DIR=/tmp
852862
AGENT_SETUP_PATH="/usr/local/gse/${NODE_TYPE}"
863+
853864
CURR_PID=$$
854865
OVERIDE=false
855866
REMOVE=false
@@ -914,6 +925,7 @@ done
914925
PKG_NAME=${NAME}-${VERSION}.tgz
915926
COMPLETE_DOWNLOAD_URL="${DOWNLOAD_URL}/agent/darwin/${CPU_ARCH}"
916927
GSE_AGENT_CONFIG_PATH="${AGENT_SETUP_PATH}/etc/${GSE_AGENT_CONFIG}"
928+
GSE_HOME=$(dirname ${AGENT_SETUP_PATH})
917929

918930
LOG_FILE="$TMP_DIR"/nm.${0##*/}.$TASK_ID
919931
DEBUG_LOG_FILE=${TMP_DIR}/nm.${0##*/}.${TASK_ID}.debug

script_tools/agent_tools/agent2/setup_proxy.sh

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,16 @@ remove_crontab () {
323323
fi
324324
}
325325

326+
remove_directory () {
327+
for dir in "$@"; do
328+
if [ -d "$dir" ]; then
329+
log remove_directory - "trying to remove directory [${dir}]"
330+
rm -rf "$dir"
331+
log remove_directory - "directory [${dir}] removed"
332+
fi
333+
done
334+
}
335+
326336
setup_startup_scripts () {
327337
check_rc_file
328338
local rcfile=$RC_LOCAL_FILE
@@ -520,7 +530,7 @@ remove_proxy () {
520530

521531
if [[ "$REMOVE" == "TRUE" ]]; then
522532
unregister_agent_id SKIP
523-
clean_up_proxy_directory
533+
remove_directory "$GSE_HOME" "$GSE_AGENT_RUN_DIR" "$GSE_AGENT_DATA_DIR" "$GSE_AGENT_LOG_DIR"
524534
log remove_proxy DONE "proxy removed"
525535
exit 0
526536
else
@@ -896,6 +906,7 @@ DEBUG_LOG_FILE=${TMP_DIR}/nm.${0##*/}.${TASK_ID}.debug
896906
PKG_NAME=${NAME}-${VERSION}.tgz
897907
COMPLETE_DOWNLOAD_URL="${DOWNLOAD_URL}/agent/linux/${CPU_ARCH}/"
898908
GSE_AGENT_CONFIG_PATH="${AGENT_SETUP_PATH}/etc/${GSE_AGENT_CONFIG}"
909+
GSE_HOME=$(dirname ${AGENT_SETUP_PATH})
899910

900911
# redirect STDOUT & STDERR to DEBUG
901912
exec &> >(tee "$DEBUG_LOG_FILE")

0 commit comments

Comments
 (0)