cli_rs: Get backing repos for eden du

Summary: See title.

Reviewed By: fanzeyi

Differential Revision: D32962761

fbshipit-source-id: 33ffec38216d81eaf8b5843a29bc6baff25a5b9d
This commit is contained in:
Grace Ku 2021-12-15 11:08:26 -08:00 committed by Facebook GitHub Bot
parent d848d329b7
commit 6724635167
2 changed files with 96 additions and 1 deletions

View File

@ -5,11 +5,14 @@
* GNU General Public License version 2.
*/
use anyhow::anyhow;
use edenfs_error::{EdenFsError, Result, ResultExt};
use edenfs_utils::path_from_bytes;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::collections::BTreeMap;
use std::fmt;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;
@ -221,8 +224,10 @@ where
/// Represents an edenfs checkout with mount information as well as information from configuration
#[derive(Serialize)]
pub struct EdenFsCheckout {
/// E.g., /data/sandcastle/boxes/fbsource
#[serde(skip)]
path: PathBuf,
/// E.g., /home/unixname/local/.eden/clients/fbsource
data_dir: PathBuf,
/// This is None when it's just configured but not actively mounted in eden
#[serde(serialize_with = "serialize_state")]
@ -234,6 +239,10 @@ pub struct EdenFsCheckout {
}
impl EdenFsCheckout {
pub fn backing_repo(&self) -> Option<PathBuf> {
self.backing_repo.clone()
}
fn from_mount_info(path: PathBuf, thrift_mount: MountInfo) -> Result<EdenFsCheckout> {
Ok(EdenFsCheckout {
path,
@ -352,3 +361,71 @@ pub async fn get_mounts(instance: &EdenFsInstance) -> Result<BTreeMap<PathBuf, E
Ok(mount_points)
}
/// If the path provided is an eden checkout, this returns an object representing that checkout.
/// Otherwise, if the path provided is not an eden checkout, this returns None.
pub fn find_checkout(instance: &EdenFsInstance, path: &Path) -> Result<EdenFsCheckout> {
// Resolve symlinks and get absolute path
let path = path.canonicalize().from_err()?;
// Check if it is a mounted checkout
let (checkout_root, checkout_state_dir) = if cfg!(windows) {
// On Windows, walk the path backwards until both parent and dir point to "C:\"
todo!("Windows not implemented yet");
} else {
// We will get an error if any of these symlinks do not exist
let eden_socket_path = fs::read_link(path.join(".eden").join("socket"));
match eden_socket_path {
Ok(_) => {
let root = fs::read_link(path.join(".eden").join("root")).ok();
let state_dir = fs::read_link(path.join(".eden").join("client")).ok();
(root, state_dir)
}
Err(_) => (None, None),
}
};
if checkout_root.is_none() {
// Find `checkout_path` that `path` is a sub path of
let all_checkouts = instance.get_configured_mounts_map()?;
if let Some(item) = all_checkouts
.iter()
.find(|&(checkout_path, _)| path.starts_with(checkout_path))
{
let (checkout_path, checkout_name) = item;
let checkout_state_dir = config_directory(instance, checkout_name);
Ok(EdenFsCheckout::from_config(
PathBuf::from(checkout_path),
checkout_state_dir.clone(),
CheckoutConfig::parse_config(checkout_state_dir)?,
))
} else {
Err(EdenFsError::Other(anyhow!(
"Checkout path {} is not handled by EdenFS",
path.display()
)))
}
} else if checkout_state_dir.is_none() {
let all_checkouts = instance.get_configured_mounts_map()?;
let checkout_path = checkout_root.unwrap();
if let Some(checkout_name) = all_checkouts.get(&checkout_path) {
let checkout_state_dir = config_directory(instance, checkout_name);
Ok(EdenFsCheckout::from_config(
checkout_path,
checkout_state_dir.clone(),
CheckoutConfig::parse_config(checkout_state_dir)?,
))
} else {
Err(EdenFsError::Other(anyhow!(
"unknown checkout {}",
checkout_path.display()
)))
}
} else {
Ok(EdenFsCheckout::from_config(
checkout_root.unwrap(),
checkout_state_dir.as_ref().unwrap().clone(),
CheckoutConfig::parse_config(checkout_state_dir.unwrap())?,
))
}
}

View File

@ -12,6 +12,7 @@ use std::path::PathBuf;
use structopt::StructOpt;
use anyhow::anyhow;
use edenfs_client::checkout::find_checkout;
use edenfs_client::EdenFsInstance;
use edenfs_error::{EdenFsError, Result};
@ -39,13 +40,14 @@ pub struct DiskUsageCmd {
}
fn write_title(title: &str) {
println!("{}", title);
println!("\n{}", title);
println!("{}", "-".repeat(title.len()));
}
#[async_trait]
impl crate::Subcommand for DiskUsageCmd {
async fn run(&self, instance: EdenFsInstance) -> Result<ExitCode> {
// Get mount configuration info
let mounts = if !self.mounts.is_empty() {
(&self.mounts).to_vec()
} else {
@ -60,11 +62,27 @@ impl crate::Subcommand for DiskUsageCmd {
config_paths
};
let mut backing_repos = Vec::new();
for mount in &mounts {
let checkout = find_checkout(&instance, mount)?;
if let Some(b) = checkout.backing_repo() {
backing_repos.push(b);
}
}
write_title("Mounts");
for path in &mounts {
println!("{}", path.display());
}
write_title("Backing repos");
for backing in backing_repos {
println!("{}", backing.display());
}
println!(
"\nCAUTION: You can lose work and break things by manually deleting data \
from the backing repo directory!"
);
Ok(0)
}
}