sapling/eden/mononoke/cmds/admin/subcommand_fsnodes.rs
Thomas Orozco f1e4f62e2d mononoke/fsnodes: expose FsnodeFile as the LeafId
Summary:
Fsnodes have a lot of data about files, but right now we can't access it
through a Fsnode lookup or a manifest walk, because the LeafId for a Fsnode is
just the content id and the file type.

This is a bit sad, because it means we e.g. cannot dump a manifest with file
sizes (D23471561 (179e4eb80e)).

Just changing the LeafId is easy, but that brings a new problem with Fsnode
derivation.

Indeed, deriving manifests normally expects us to have the "derive leaf"
function produce a LeafId (so we'd want to produce a `FsnodeFile`), but in
Fsnodes, this currently happens in deriving trees instead.

Unfortunately, we cannot easily just move the code that produces `FsnodeFile`
from the tree derivation to the leaf derivation, that is, do:

```
fn check_fsnode_leaf(
    leaf_info: LeafInfo<FsnodeFile, (ContentId, FileType)>,
) -> impl Future<Item = (Option<FsnodeSummary>, FsnodeFile), Error = Error>
```

Indeed, the performance of Fsnode derivation relies on all the leaves for a
given tree being derived together with the tree and its parents in context.

So, we'd need the ability for deriving a new leaf to return something different
from the actual leaf id. This means we want to return a `(ContentId,
FileType)`, even though our `LeafId` is a `FsnodeFile`.

To do this, this diff introduces a new `IntermediateLeafId` type in the
derivation. This represents the type of the leaf that is passed from deriving a
leaf to deriving a tree. We need to be able to turn a real `LeafId` into it,
because sometimes we don't re-derive leaves.

I think we could also refactor some of the code that passes a context here to
just do this through the `IntermediateLeafId`, but I didn't look into this too
much.

So, this diff does that, and uses it in Mononoke Admin so we can print file
sizes.

Reviewed By: StanislavGlebik

Differential Revision: D23497754

fbshipit-source-id: 2fc480be0b1e4d3d261da1d4d3dcd9c7b8501b9b
2020-09-04 06:30:18 -07:00

112 lines
3.2 KiB
Rust

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
use crate::error::SubcommandError;
use anyhow::Error;
use blobrepo::BlobRepo;
use clap::{App, Arg, ArgMatches, SubCommand};
use cmdlib::{args, helpers};
use context::CoreContext;
use derived_data::BonsaiDerived;
use fbinit::FacebookInit;
use futures::{
compat::{Future01CompatExt, Stream01CompatExt},
stream::StreamExt,
};
use manifest::{Entry, ManifestOps, PathOrPrefix};
use fsnodes::RootFsnodeId;
use mononoke_types::{ChangesetId, MPath};
use slog::{info, Logger};
pub const FSNODES: &str = "fsnodes";
const COMMAND_TREE: &str = "tree";
const ARG_CSID: &str = "csid";
const ARG_PATH: &str = "path";
pub fn build_subcommand<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name(FSNODES)
.about("inspect fsnodes")
.subcommand(
SubCommand::with_name(COMMAND_TREE)
.about("recursively list all fsnode entries starting with prefix")
.arg(
Arg::with_name(ARG_CSID)
.help("{hg|bonsai} changeset id or bookmark name")
.required(true),
)
.arg(Arg::with_name(ARG_PATH).help("path")),
)
}
pub async fn subcommand_fsnodes<'a>(
fb: FacebookInit,
logger: Logger,
matches: &'a ArgMatches<'_>,
sub_matches: &'a ArgMatches<'_>,
) -> Result<(), SubcommandError> {
args::init_cachelib(fb, &matches, None);
let repo = args::open_repo(fb, &logger, &matches).compat().await?;
let ctx = CoreContext::new_with_logger(fb, logger.clone());
match sub_matches.subcommand() {
(COMMAND_TREE, Some(matches)) => {
let hash_or_bookmark = String::from(matches.value_of(ARG_CSID).unwrap());
let path = matches.value_of(ARG_PATH).map(MPath::new).transpose()?;
let csid = helpers::csid_resolve(ctx.clone(), repo.clone(), hash_or_bookmark)
.compat()
.await?;
subcommand_tree(&ctx, &repo, csid, path).await?;
Ok(())
}
_ => Err(SubcommandError::InvalidArgs),
}
}
async fn subcommand_tree(
ctx: &CoreContext,
repo: &BlobRepo,
csid: ChangesetId,
path: Option<MPath>,
) -> Result<(), Error> {
let root = RootFsnodeId::derive(ctx.clone(), repo.clone(), csid)
.compat()
.await?;
info!(ctx.logger(), "ROOT: {:?}", root);
info!(ctx.logger(), "PATH: {:?}", path);
let mut stream = root
.fsnode_id()
.find_entries(
ctx.clone(),
repo.get_blobstore(),
vec![PathOrPrefix::Prefix(path)],
)
.compat();
while let Some((path, entry)) = stream.next().await.transpose()? {
match entry {
Entry::Tree(..) => {}
Entry::Leaf(file) => {
println!(
"{}\t{}\t{}\t{}",
MPath::display_opt(path.as_ref()),
file.content_id(),
file.file_type(),
file.size(),
);
}
};
}
Ok(())
}