From c19681bc12dc7749e50ad3f119a633daa582a7b0 Mon Sep 17 00:00:00 2001 From: Alex Chi Z Date: Wed, 28 Jun 2023 11:39:07 -0400 Subject: [PATCH] neon_local: support force init (#4363) When we use local SSD for bench and create `.neon` directory before we do `cargo neon init`, the initialization process will error due to directory already exists. This PR adds a flag `--force` that removes everything inside the directory if `.neon` already exists. --------- Signed-off-by: Alex Chi Z. --- control_plane/src/bin/neon_local.rs | 11 +++++++++- control_plane/src/local_env.rs | 34 +++++++++++++++++++++++------ 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/control_plane/src/bin/neon_local.rs b/control_plane/src/bin/neon_local.rs index 52af936d7b..8995a18564 100644 --- a/control_plane/src/bin/neon_local.rs +++ b/control_plane/src/bin/neon_local.rs @@ -308,7 +308,8 @@ fn handle_init(init_match: &ArgMatches) -> anyhow::Result { let mut env = LocalEnv::parse_config(&toml_file).context("Failed to create neon configuration")?; - env.init(pg_version) + let force = init_match.get_flag("force"); + env.init(pg_version, force) .context("Failed to initialize neon repository")?; // Initialize pageserver, create initial tenant and timeline. @@ -1013,6 +1014,13 @@ fn cli() -> Command { .help("If set, the node will be a hot replica on the specified timeline") .required(false); + let force_arg = Arg::new("force") + .value_parser(value_parser!(bool)) + .long("force") + .action(ArgAction::SetTrue) + .help("Force initialization even if the repository is not empty") + .required(false); + Command::new("Neon CLI") .arg_required_else_help(true) .version(GIT_VERSION) @@ -1028,6 +1036,7 @@ fn cli() -> Command { .value_name("config"), ) .arg(pg_version_arg.clone()) + .arg(force_arg) ) .subcommand( Command::new("timeline") diff --git a/control_plane/src/local_env.rs b/control_plane/src/local_env.rs index df70cb3139..208eb9e7ec 100644 --- a/control_plane/src/local_env.rs +++ b/control_plane/src/local_env.rs @@ -364,7 +364,7 @@ impl LocalEnv { // // Initialize a new Neon repository // - pub fn init(&mut self, pg_version: u32) -> anyhow::Result<()> { + pub fn init(&mut self, pg_version: u32, force: bool) -> anyhow::Result<()> { // check if config already exists let base_path = &self.base_data_dir; ensure!( @@ -372,11 +372,29 @@ impl LocalEnv { "repository base path is missing" ); - ensure!( - !base_path.exists(), - "directory '{}' already exists. Perhaps already initialized?", - base_path.display() - ); + if base_path.exists() { + if force { + println!("removing all contents of '{}'", base_path.display()); + // instead of directly calling `remove_dir_all`, we keep the original dir but removing + // all contents inside. This helps if the developer symbol links another directory (i.e., + // S3 local SSD) to the `.neon` base directory. + for entry in std::fs::read_dir(base_path)? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + fs::remove_dir_all(&path)?; + } else { + fs::remove_file(&path)?; + } + } + } else { + bail!( + "directory '{}' already exists. Perhaps already initialized? (Hint: use --force to remove all contents)", + base_path.display() + ); + } + } + if !self.pg_bin_dir(pg_version)?.join("postgres").exists() { bail!( "Can't find postgres binary at {}", @@ -392,7 +410,9 @@ impl LocalEnv { } } - fs::create_dir(base_path)?; + if !base_path.exists() { + fs::create_dir(base_path)?; + } // Generate keypair for JWT. //