meta_srv/service/admin/
maintenance.rs1use std::collections::HashMap;
16
17use common_meta::key::runtime_switch::RuntimeSwitchManagerRef;
18use common_telemetry::{info, warn};
19use serde::{Deserialize, Serialize};
20use snafu::{OptionExt, ResultExt};
21use tonic::codegen::http;
22use tonic::codegen::http::Response;
23
24use crate::error::{
25 self, MissingRequiredParameterSnafu, ParseBoolSnafu, Result, RuntimeSwitchManagerSnafu,
26 UnsupportedSnafu,
27};
28use crate::service::admin::util::{to_json_response, to_not_found_response};
29use crate::service::admin::HttpHandler;
30
31#[derive(Clone)]
32pub struct MaintenanceHandler {
33 pub manager: RuntimeSwitchManagerRef,
34}
35
36#[derive(Debug, Serialize, Deserialize)]
37struct MaintenanceResponse {
38 enabled: bool,
39}
40
41impl TryFrom<MaintenanceResponse> for String {
42 type Error = error::Error;
43
44 fn try_from(response: MaintenanceResponse) -> Result<Self> {
45 serde_json::to_string(&response).context(error::SerializeToJsonSnafu {
46 input: format!("{response:?}"),
47 })
48 }
49}
50
51impl MaintenanceHandler {
52 async fn get_maintenance(&self) -> crate::Result<MaintenanceResponse> {
53 let enabled = self
54 .manager
55 .maintenance_mode()
56 .await
57 .context(RuntimeSwitchManagerSnafu)?;
58 Ok(MaintenanceResponse { enabled })
59 }
60
61 async fn set_maintenance(&self) -> crate::Result<MaintenanceResponse> {
62 self.manager
63 .set_maintenance_mode()
64 .await
65 .context(RuntimeSwitchManagerSnafu)?;
66 info!("Enable the maintenance mode.");
68 Ok(MaintenanceResponse { enabled: true })
69 }
70
71 async fn unset_maintenance(&self) -> crate::Result<MaintenanceResponse> {
72 self.manager
73 .unset_maintenance_mode()
74 .await
75 .context(RuntimeSwitchManagerSnafu)?;
76 info!("Disable the maintenance mode.");
78 Ok(MaintenanceResponse { enabled: false })
79 }
80
81 async fn handle_legacy_maintenance(
82 &self,
83 params: &HashMap<String, String>,
84 ) -> crate::Result<MaintenanceResponse> {
85 let enable = get_enable_from_params(params)?;
86 if enable {
87 self.set_maintenance().await
88 } else {
89 self.unset_maintenance().await
90 }
91 }
92}
93
94fn get_enable_from_params(params: &HashMap<String, String>) -> crate::Result<bool> {
95 params
96 .get("enable")
97 .map(|v| v.parse::<bool>())
98 .context(MissingRequiredParameterSnafu { param: "enable" })?
99 .context(ParseBoolSnafu {
100 err_msg: "'enable' must be 'true' or 'false'",
101 })
102}
103
104const MAINTENANCE_PATH: &str = "maintenance";
105const ENABLE_SUFFIX: &str = "enable";
106const DISABLE_SUFFIX: &str = "disable";
107const STATUS_SUFFIX: &str = "status";
108
109#[async_trait::async_trait]
110impl HttpHandler for MaintenanceHandler {
111 async fn handle(
114 &self,
115 path: &str,
116 method: http::Method,
117 params: &HashMap<String, String>,
118 ) -> crate::Result<Response<String>> {
119 match method {
120 http::Method::GET => {
121 if path.ends_with(STATUS_SUFFIX) {
122 let response = self.get_maintenance().await?;
124 to_json_response(response)
125 } else if path.ends_with(MAINTENANCE_PATH) && params.is_empty() {
126 let response = self.get_maintenance().await?;
128 to_json_response(response)
129 } else if path.ends_with(MAINTENANCE_PATH) {
130 warn!(
132 "Found URL parameters in '/admin/maintenance' request, it's deprecated, will be removed in the future"
133 );
134 let response = self.handle_legacy_maintenance(params).await?;
137 to_json_response(response)
138 } else {
139 to_not_found_response()
140 }
141 }
142 http::Method::PUT => {
143 if path.ends_with(MAINTENANCE_PATH) {
145 warn!("Found PUT request to '/admin/maintenance', it's deprecated, will be removed in the future");
146 let response = self.handle_legacy_maintenance(params).await?;
147 to_json_response(response)
148 } else {
149 to_not_found_response()
150 }
151 }
152 http::Method::POST => {
153 if path.ends_with(ENABLE_SUFFIX) {
155 let response = self.set_maintenance().await?;
156 to_json_response(response)
157 } else if path.ends_with(DISABLE_SUFFIX) {
158 let response = self.unset_maintenance().await?;
160 to_json_response(response)
161 } else if path.ends_with(MAINTENANCE_PATH) {
162 warn!("Found PUT request to '/admin/maintenance', it's deprecated, will be removed in the future");
164 let response = self.handle_legacy_maintenance(params).await?;
165 to_json_response(response)
166 } else {
167 to_not_found_response()
168 }
169 }
170 _ => UnsupportedSnafu {
171 operation: format!("http method {method}"),
172 }
173 .fail(),
174 }
175 }
176}