meta_srv/service/admin/
maintenance.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::collections::HashMap;
16
17use common_meta::key::maintenance::MaintenanceModeManagerRef;
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, InvalidHttpBodySnafu, MaintenanceModeManagerSnafu, MissingRequiredParameterSnafu,
26    ParseBoolSnafu, Result, UnsupportedSnafu,
27};
28use crate::service::admin::HttpHandler;
29
30#[derive(Clone)]
31pub struct MaintenanceHandler {
32    pub manager: MaintenanceModeManagerRef,
33}
34
35#[derive(Debug, Serialize, Deserialize)]
36struct MaintenanceResponse {
37    enabled: bool,
38}
39
40impl TryFrom<MaintenanceResponse> for String {
41    type Error = error::Error;
42
43    fn try_from(response: MaintenanceResponse) -> Result<Self> {
44        serde_json::to_string(&response).context(error::SerializeToJsonSnafu {
45            input: format!("{response:?}"),
46        })
47    }
48}
49
50impl MaintenanceHandler {
51    async fn get_maintenance(&self) -> crate::Result<Response<String>> {
52        let enabled = self
53            .manager
54            .maintenance_mode()
55            .await
56            .context(MaintenanceModeManagerSnafu)?;
57        let response = MaintenanceResponse { enabled }.try_into()?;
58        http::Response::builder()
59            .status(http::StatusCode::OK)
60            .body(response)
61            .context(InvalidHttpBodySnafu)
62    }
63
64    async fn set_maintenance(
65        &self,
66        params: &HashMap<String, String>,
67    ) -> crate::Result<Response<String>> {
68        let enable = params
69            .get("enable")
70            .map(|v| v.parse::<bool>())
71            .context(MissingRequiredParameterSnafu { param: "enable" })?
72            .context(ParseBoolSnafu {
73                err_msg: "'enable' must be 'true' or 'false'",
74            })?;
75
76        if enable {
77            self.manager
78                .set_maintenance_mode()
79                .await
80                .context(MaintenanceModeManagerSnafu)?;
81            info!("Enable the maintenance mode.");
82        } else {
83            self.manager
84                .unset_maintenance_mode()
85                .await
86                .context(MaintenanceModeManagerSnafu)?;
87            info!("Disable the maintenance mode.");
88        };
89
90        let response = MaintenanceResponse { enabled: enable }.try_into()?;
91        http::Response::builder()
92            .status(http::StatusCode::OK)
93            .body(response)
94            .context(InvalidHttpBodySnafu)
95    }
96}
97
98#[async_trait::async_trait]
99impl HttpHandler for MaintenanceHandler {
100    async fn handle(
101        &self,
102        _: &str,
103        method: http::Method,
104        params: &HashMap<String, String>,
105    ) -> crate::Result<Response<String>> {
106        match method {
107            http::Method::GET => {
108                if params.is_empty() {
109                    self.get_maintenance().await
110                } else {
111                    warn!(
112                        "Found URL parameters in '/admin/maintenance' request, it's deprecated, will be removed in the future"
113                    );
114                    // The old version operator will send GET request with URL parameters,
115                    // so we need to support it.
116                    self.set_maintenance(params).await
117                }
118            }
119            http::Method::PUT => {
120                warn!("Found PUT request to '/admin/maintenance', it's deprecated, will be removed in the future");
121                self.set_maintenance(params).await
122            }
123            http::Method::POST => self.set_maintenance(params).await,
124            _ => UnsupportedSnafu {
125                operation: format!("http method {method}"),
126            }
127            .fail(),
128        }
129    }
130}