From 7e6eff4969cc955800e4a0848485ac9432b71c13 Mon Sep 17 00:00:00 2001 From: Konstantin Knizhnik Date: Tue, 1 Mar 2022 18:07:03 +0300 Subject: [PATCH] Save tenant config in pageserver directory --- control_plane/src/storage.rs | 15 ++++++++++++++- pageserver/src/config.rs | 34 +++++++++++++++++++++++++++++++++- zenith/src/main.rs | 9 +++++++-- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/control_plane/src/storage.rs b/control_plane/src/storage.rs index 694f73c4e0..296c227c01 100644 --- a/control_plane/src/storage.rs +++ b/control_plane/src/storage.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::io::Write; use std::net::TcpStream; use std::path::PathBuf; @@ -342,10 +343,22 @@ impl PageServerNode { pub fn tenant_create( &self, new_tenant_id: Option, + settings: HashMap<&str, &str> ) -> anyhow::Result> { let tenant_id_string = self .http_request(Method::POST, format!("{}/tenant", self.http_base_url)) - .json(&TenantCreateRequest::new(tenantid)) + .json(&TenantCreateRequest { + new_tenant_id, + checkpoint_distance: settings + .get("checkpoint_distance") + .map(|x| x.parse::().unwrap()), + compaction_period: settings.get("compaction_period").map(|x| x.to_string()), + gc_horizon: settings + .get("gc_horizon") + .map(|x| x.parse::().unwrap()), + gc_period: settings.get("gc_period").map(|x| x.to_string()), + pitr_interval: settings.get("pitr_interval").map(|x| x.to_string()), + }) .send()? .error_from_body()? .json::>()?; diff --git a/pageserver/src/config.rs b/pageserver/src/config.rs index 6742e0391e..d6fdb93106 100644 --- a/pageserver/src/config.rs +++ b/pageserver/src/config.rs @@ -5,19 +5,25 @@ //! See also `settings.md` for better description on every parameter. use anyhow::{bail, ensure, Context, Result}; +use serde::{Deserialize, Serialize}; use toml_edit; use toml_edit::{Document, Item}; +use tracing::*; +use zenith_utils::bin_ser::BeSer; use zenith_utils::postgres_backend::AuthType; use zenith_utils::zid::{ZNodeId, ZTenantId, ZTimelineId}; use std::convert::TryInto; use std::env; +use std::fs::OpenOptions; +use std::io::Write; use std::num::{NonZeroU32, NonZeroUsize}; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::time::Duration; use crate::layered_repository::TIMELINES_SEGMENT_NAME; +use crate::virtual_file::VirtualFile; pub mod defaults { use const_format::formatcp; @@ -139,7 +145,9 @@ pub struct PageServerConf { pub remote_storage_config: Option, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub const TENANT_CONFIG_NAME: &str = "config"; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub struct TenantConf { pub checkpoint_distance: u64, pub compaction_period: Duration, @@ -174,6 +182,30 @@ impl BuilderValue { Self::NotSet => Err(err), } } + + pub fn save(&self, conf: &'static PageServerConf, tenantid: ZTenantId) -> Result<()> { + let _enter = info_span!("saving tenant config").entered(); + let path = conf.tenant_path(&tenantid).join(TENANT_CONFIG_NAME); + let mut file = + VirtualFile::open_with_options(&path, OpenOptions::new().write(true).create_new(true))?; + let config_bytes = self.ser()?; + if file.write(&config_bytes)? != config_bytes.len() { + bail!("Could not write all the metadata bytes in a single call"); + } + file.sync_all()?; + Ok(()) + } + + pub fn load(conf: &'static PageServerConf, tenantid: ZTenantId) -> Result { + let _enter = info_span!("loading tenant config").entered(); + let path = conf.tenant_path(&tenantid).join(TENANT_CONFIG_NAME); + let content = std::fs::read(&path); + match content { + Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(Self::from(conf)), + Ok(config_bytes) => Ok(TenantConf::des(&config_bytes)?), + Err(err) => bail!(err), + } + } } // needed to simplify config construction diff --git a/zenith/src/main.rs b/zenith/src/main.rs index f5d4184e63..ca169867f6 100644 --- a/zenith/src/main.rs +++ b/zenith/src/main.rs @@ -164,7 +164,8 @@ fn main() -> Result<()> { .subcommand(App::new("create") .arg(tenant_id_arg.clone()) .arg(timeline_id_arg.clone().help("Use a specific timeline id when creating a tenant and its initial timeline")) - ) + .arg(Arg::new("config").short('c').takes_value(true).multiple_occurrences(true).required(false)) + ) ) .subcommand( App::new("pageserver") @@ -521,8 +522,12 @@ fn handle_tenant(tenant_match: &ArgMatches, env: &mut local_env::LocalEnv) -> Re } Some(("create", create_match)) => { let initial_tenant_id = parse_tenant_id(create_match)?; + let tenant_conf: HashMap<_, _> = create_match + .values_of("config") + .map(|vals| vals.flat_map(|c| c.split_once(':')).collect()) + .unwrap_or(HashMap::new()); let new_tenant_id = pageserver - .tenant_create(initial_tenant_id)? + .tenant_create(initial_tenant_id, tenant_conf)? .ok_or_else(|| { anyhow!("Tenant with id {:?} was already created", initial_tenant_id) })?;