diff --git a/control_plane/src/local_env.rs b/control_plane/src/local_env.rs index ce340cb198..38c75948c6 100644 --- a/control_plane/src/local_env.rs +++ b/control_plane/src/local_env.rs @@ -39,9 +39,11 @@ pub struct LocalEnv { // Path to pageserver binary. pub zenith_distrib_dir: PathBuf, - // keeping tenant id in config to reduce copy paste when running zenith locally with single tenant - #[serde(with = "hex")] - pub tenantid: ZTenantId, + // Default tenant ID to use with the 'zenith' command line utility, when + // --tenantid is not explicitly specified. + #[serde(with = "opt_tenantid_serde")] + #[serde(default)] + pub default_tenantid: Option, // jwt auth token used for communication with pageserver pub auth_token: String, @@ -173,7 +175,7 @@ pub fn init( pg_distrib_dir, zenith_distrib_dir, base_data_dir: base_path, - tenantid, + default_tenantid: Some(tenantid), auth_token, auth_type, private_key_path, @@ -204,3 +206,30 @@ pub fn load_config() -> Result { let config = fs::read_to_string(repopath.join("config"))?; toml::from_str(config.as_str()).map_err(|e| e.into()) } + +/// Serde routines for Option. The serialized form is a hex string. +mod opt_tenantid_serde { + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use std::str::FromStr; + use zenith_utils::zid::ZTenantId; + + pub fn serialize(tenantid: &Option, ser: S) -> Result + where + S: Serializer, + { + tenantid.map(|t| t.to_string()).serialize(ser) + } + + pub fn deserialize<'de, D>(des: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let s: Option = Option::deserialize(des)?; + if let Some(s) = s { + return Ok(Some( + ZTenantId::from_str(&s).map_err(serde::de::Error::custom)?, + )); + } + Ok(None) + } +} diff --git a/zenith/src/main.rs b/zenith/src/main.rs index 214e71c1c7..63a6a72808 100644 --- a/zenith/src/main.rs +++ b/zenith/src/main.rs @@ -1,4 +1,4 @@ -use anyhow::anyhow; +use anyhow::{anyhow, bail}; use anyhow::{Context, Result}; use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; use control_plane::compute::ComputeControlPlane; @@ -178,7 +178,8 @@ fn main() -> Result<()> { ("init", Some(init_match)) => { let pageserver = PageServerNode::from_env(&env); if let Err(e) = pageserver.init( - Some(&env.tenantid.to_string()), + // default_tenantid was generated before the `local_env::init` call above + Some(&env.default_tenantid.unwrap().to_string()), init_match.is_present("enable-auth"), ) { eprintln!("pageserver init failed: {}", e); @@ -380,6 +381,17 @@ fn get_branch_infos( Ok(branch_infos) } +// Helper function to parse --tenantid option, or get the default from config file +fn get_tenantid(sub_match: &ArgMatches, env: &local_env::LocalEnv) -> Result { + if let Some(tenantid_cmd) = sub_match.value_of("tenantid") { + Ok(ZTenantId::from_str(tenantid_cmd)?) + } else if let Some(tenantid_conf) = env.default_tenantid { + Ok(tenantid_conf) + } else { + bail!("No tenantid. Use --tenantid, or set 'default_tenantid' in the config file"); + } +} + fn handle_tenant(tenant_match: &ArgMatches, env: &local_env::LocalEnv) -> Result<()> { let pageserver = PageServerNode::from_env(env); match tenant_match.subcommand() { @@ -405,22 +417,18 @@ fn handle_tenant(tenant_match: &ArgMatches, env: &local_env::LocalEnv) -> Result fn handle_branch(branch_match: &ArgMatches, env: &local_env::LocalEnv) -> Result<()> { let pageserver = PageServerNode::from_env(env); + let tenantid = get_tenantid(branch_match, env)?; + if let Some(branchname) = branch_match.value_of("branchname") { let startpoint_str = branch_match .value_of("start-point") .ok_or_else(|| anyhow!("Missing start-point"))?; - let tenantid: ZTenantId = branch_match - .value_of("tenantid") - .map_or(Ok(env.tenantid), |value| value.parse())?; let branch = pageserver.branch_create(branchname, startpoint_str, &tenantid)?; println!( "Created branch '{}' at {:?} for tenant: {}", branch.name, branch.latest_valid_lsn, tenantid, ); } else { - let tenantid: ZTenantId = branch_match - .value_of("tenantid") - .map_or(Ok(env.tenantid), |value| value.parse())?; // No arguments, list branches for tenant let branches = pageserver.branch_list(&tenantid)?; print_branches_tree(branches)?; @@ -434,9 +442,7 @@ fn handle_pg(pg_match: &ArgMatches, env: &local_env::LocalEnv) -> Result<()> { match pg_match.subcommand() { ("list", Some(list_match)) => { - let tenantid: ZTenantId = list_match - .value_of("tenantid") - .map_or(Ok(env.tenantid), |value| value.parse())?; + let tenantid = get_tenantid(list_match, env)?; let branch_infos = get_branch_infos(env, &tenantid).unwrap_or_else(|e| { eprintln!("Failed to load branch info: {}", e); @@ -468,9 +474,7 @@ fn handle_pg(pg_match: &ArgMatches, env: &local_env::LocalEnv) -> Result<()> { } } ("create", Some(create_match)) => { - let tenantid: ZTenantId = create_match - .value_of("tenantid") - .map_or(Ok(env.tenantid), |value| value.parse())?; + let tenantid = get_tenantid(create_match, env)?; let node_name = create_match.value_of("node").unwrap_or("main"); let timeline_name = create_match.value_of("timeline").unwrap_or(node_name); @@ -481,9 +485,7 @@ fn handle_pg(pg_match: &ArgMatches, env: &local_env::LocalEnv) -> Result<()> { cplane.new_node(tenantid, node_name, timeline_name, port)?; } ("start", Some(start_match)) => { - let tenantid: ZTenantId = start_match - .value_of("tenantid") - .map_or(Ok(env.tenantid), |value| value.parse())?; + let tenantid = get_tenantid(start_match, env)?; let node_name = start_match.value_of("node").unwrap_or("main"); let timeline_name = start_match.value_of("timeline"); @@ -523,11 +525,9 @@ fn handle_pg(pg_match: &ArgMatches, env: &local_env::LocalEnv) -> Result<()> { } } ("stop", Some(stop_match)) => { + let tenantid = get_tenantid(stop_match, env)?; let node_name = stop_match.value_of("node").unwrap_or("main"); let destroy = stop_match.is_present("destroy"); - let tenantid: ZTenantId = stop_match - .value_of("tenantid") - .map_or(Ok(env.tenantid), |value| value.parse())?; let node = cplane .nodes