use std::collections::HashSet; use std::sync::Arc; use super::error::ConfigError; use super::{storageclass, Config, GLOBAL_StorageClass, KVS}; use crate::config::error::is_not_found; use crate::disk::RUSTFS_META_BUCKET; use crate::error::{Error, Result}; use crate::store_api::{HTTPRangeSpec, ObjectInfo, ObjectOptions, PutObjReader, StorageAPI}; use crate::store_err::is_err_object_not_found; use crate::utils::path::SLASH_SEPARATOR; use http::HeaderMap; use lazy_static::lazy_static; use s3s::dto::StreamingBlob; use s3s::Body; use tracing::{error, warn}; pub const CONFIG_PREFIX: &str = "config"; const CONFIG_FILE: &str = "config.json"; pub const STORAGE_CLASS_SUB_SYS: &str = "storage_class"; pub const DEFAULT_KV_KEY: &str = "_"; lazy_static! { static ref CONFIG_BUCKET: String = format!("{}{}{}", RUSTFS_META_BUCKET, SLASH_SEPARATOR, CONFIG_PREFIX); static ref SubSystemsDynamic: HashSet = { let mut h = HashSet::new(); h.insert(STORAGE_CLASS_SUB_SYS.to_owned()); h }; } pub async fn read_config(api: Arc, file: &str) -> Result> { let (data, _obj) = read_config_with_metadata(api, file, &ObjectOptions::default()).await?; Ok(data) } async fn read_config_with_metadata( api: Arc, file: &str, opts: &ObjectOptions, ) -> Result<(Vec, ObjectInfo)> { let range = HTTPRangeSpec::nil(); let h = HeaderMap::new(); let mut rd = api .get_object_reader(RUSTFS_META_BUCKET, file, range, h, opts) .await .map_err(|err| { if is_err_object_not_found(&err) { Error::new(ConfigError::NotFound) } else { err } })?; let data = rd.read_all().await?; if data.is_empty() { return Err(Error::new(ConfigError::NotFound)); } Ok((data, rd.object_info)) } pub async fn save_config(api: Arc, file: &str, data: &[u8]) -> Result<()> { save_config_with_opts( api, file, data, &ObjectOptions { max_parity: true, ..Default::default() }, ) .await } async fn save_config_with_opts(api: Arc, file: &str, data: &[u8], opts: &ObjectOptions) -> Result<()> { let _ = api .put_object( RUSTFS_META_BUCKET, file, &mut PutObjReader::new(StreamingBlob::from(Body::from(data.to_vec())), data.len()), opts, ) .await?; Ok(()) } fn new_server_config() -> Config { Config::new() } async fn new_and_save_server_config(api: Arc) -> Result { let mut cfg = new_server_config(); lookup_configs(&mut cfg, api.clone()).await; save_server_config(api, &cfg).await?; Ok(cfg) } pub async fn read_config_without_migrate(api: Arc) -> Result { let config_file = format!("{}{}{}", CONFIG_PREFIX, SLASH_SEPARATOR, CONFIG_FILE); let data = match read_config(api.clone(), config_file.as_str()).await { Ok(res) => res, Err(err) => { if is_not_found(&err) { warn!("config not found, start to init"); let cfg = new_and_save_server_config(api).await?; warn!("config init done"); return Ok(cfg); } else { error!("read config err {:?}", &err); return Err(err); } } }; read_server_config(api, data.as_slice()).await } async fn read_server_config(api: Arc, data: &[u8]) -> Result { let cfg = { if data.is_empty() { let config_file = format!("{}{}{}", CONFIG_PREFIX, SLASH_SEPARATOR, CONFIG_FILE); let cfg_data = match read_config(api.clone(), config_file.as_str()).await { Ok(res) => res, Err(err) => { if is_not_found(&err) { warn!("config not found init start"); let cfg = new_and_save_server_config(api).await?; warn!("config not found init done"); return Ok(cfg); } else { error!("read config err {:?}", &err); return Err(err); } } }; // TODO: decrypt Config::unmarshal(cfg_data.as_slice())? } else { Config::unmarshal(data)? } }; Ok(cfg.merge()) } async fn save_server_config(api: Arc, cfg: &Config) -> Result<()> { let data = cfg.marshal()?; let config_file = format!("{}{}{}", CONFIG_PREFIX, SLASH_SEPARATOR, CONFIG_FILE); save_config(api, &config_file, data.as_slice()).await } pub async fn lookup_configs(cfg: &mut Config, api: Arc) { // TODO: from etcd if let Err(err) = apply_dynamic_config(cfg, api).await { error!("apply_dynamic_config err {:?}", &err); } } async fn apply_dynamic_config(cfg: &mut Config, api: Arc) -> Result<()> { for key in SubSystemsDynamic.iter() { apply_dynamic_config_for_sub_sys(cfg, api.clone(), key).await?; } Ok(()) } async fn apply_dynamic_config_for_sub_sys(cfg: &mut Config, api: Arc, subsys: &str) -> Result<()> { let set_drive_counts = api.set_drive_counts(); if subsys == STORAGE_CLASS_SUB_SYS { let kvs = match cfg.get_value(STORAGE_CLASS_SUB_SYS, DEFAULT_KV_KEY) { Some(res) => res, None => KVS::new(), }; for (i, count) in set_drive_counts.iter().enumerate() { match storageclass::lookup_config(&kvs, *count) { Ok(res) => { if i == 0 && GLOBAL_StorageClass.get().is_none() { if let Err(r) = GLOBAL_StorageClass.set(res) { error!("GLOBAL_StorageClass.set failed {:?}", r); } } } Err(err) => { error!("init storageclass err:{:?}", &err); break; } } } } Ok(()) }