Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ls-apis apis should show all dep paths and include Crucible's notify-nexus feature #6812

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
11 changes: 11 additions & 0 deletions dev-tools/ls-apis/api-manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,17 @@ note = """
nexus-types depends on gateway-client for defining some types.
"""

[[dependency_filter_rules]]
ancestor = "internal-dns"
client = "dns-service-client"
evaluation = "bogus"
note = """
Past versions of internal-dns (which does not exist any more) depended on
dns-service-client for defining some types. We can remove this when other repos
that depend on Omicron have updated past the removal of the "internal-dns"
package.
"""

[[dependency_filter_rules]]
ancestor = "nexus-types"
client = "dns-service-client"
Expand Down
23 changes: 19 additions & 4 deletions dev-tools/ls-apis/src/bin/ls-apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,29 @@ fn run_apis(apis: &SystemApis, args: ShowDepsArgs) -> Result<()> {
let metadata = apis.api_metadata();
for api in metadata.apis() {
println!("{} (client: {})", api.label, api.client_package_name);
for (s, path) in
for (s, dep_paths) in
apis.api_consumers(&api.client_package_name, args.filter)?
{
let (repo_name, package_path) = apis.package_label(s)?;
println!(" consumed by: {} ({}/{})", s, repo_name, package_path);
println!(
" consumed by: {} ({}/{}) via {} path{}",
s,
repo_name,
package_path,
dep_paths.len(),
if dep_paths.len() == 1 { "" } else { "s" },
);
if args.show_deps {
for p in path.nodes() {
println!(" via {}", p);
for (i, dep_path) in dep_paths.iter().enumerate() {
let label = if dep_paths.len() > 1 {
format!(" path {}", i + 1)
} else {
String::new()
};

for p in dep_path.nodes() {
println!(" via{}: {}", label, p);
}
}
}
}
Expand Down
18 changes: 11 additions & 7 deletions dev-tools/ls-apis/src/cargo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use anyhow::bail;
use anyhow::{anyhow, ensure, Context, Result};
use camino::Utf8Path;
use camino::Utf8PathBuf;
use cargo_metadata::Package;
use cargo_metadata::{CargoOpt, Package};
use cargo_metadata::{DependencyKind, PackageId};
use std::collections::BTreeSet;
use std::collections::{BTreeMap, VecDeque};
Expand Down Expand Up @@ -50,6 +50,7 @@ impl Workspace {
pub fn load(
name: &str,
manifest_path: Option<&Utf8Path>,
extra_features: Option<CargoOpt>,
ignored_non_clients: &BTreeSet<ClientPackageName>,
) -> Result<Self> {
eprintln!(
Expand All @@ -65,6 +66,9 @@ impl Workspace {
if let Some(manifest_path) = manifest_path {
cmd.manifest_path(manifest_path);
}
if let Some(extra_features) = extra_features {
cmd.features(extra_features);
}
let metadata = cmd.exec().context("loading metadata")?;
let workspace_root = metadata.workspace_root;

Expand Down Expand Up @@ -261,11 +265,6 @@ impl Workspace {
while let Some(Remaining { node: next, path }) = remaining.pop() {
for d in &next.deps {
let did = &d.pkg;
if seen.contains(did) {
continue;
}

seen.insert(did.clone());
if !d.dep_kinds.iter().any(|k| {
matches!(
k.kind,
Expand All @@ -283,8 +282,13 @@ impl Workspace {
let dep_pkg = self.packages_by_id.get(did).unwrap();
let dep_node = self.nodes_by_id.get(did).unwrap();
func(dep_pkg, &path);
if seen.contains(did) {
continue;
}

seen.insert(did.clone());
let dep_path = path.with_dependency_on(did.clone());
remaining.push(Remaining { node: dep_node, path: dep_path })
remaining.push(Remaining { node: dep_node, path: dep_path });
}
}

Expand Down
11 changes: 5 additions & 6 deletions dev-tools/ls-apis/src/system_apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ impl SystemApis {
&self,
client: &ClientPackageName,
filter: ApiDependencyFilter,
) -> Result<impl Iterator<Item = (&ServerComponentName, &DepPath)> + '_>
) -> Result<impl Iterator<Item = (&ServerComponentName, Vec<&DepPath>)> + '_>
{
let mut rv = Vec::new();

Expand All @@ -253,21 +253,20 @@ impl SystemApis {
};

for (server_pkgname, dep_paths) in api_consumers {
let mut include = None;
let mut include = Vec::new();
for p in dep_paths {
if filter.should_include(
&self.api_metadata,
&self.workspaces,
&client,
p,
)? {
include = Some(p);
break;
include.push(p);
}
}

if let Some(p) = include {
rv.push((server_pkgname, p))
if !include.is_empty() {
rv.push((server_pkgname, include))
}
}

Expand Down
49 changes: 40 additions & 9 deletions dev-tools/ls-apis/src/workspaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::cargo::Workspace;
use crate::ClientPackageName;
use anyhow::{anyhow, ensure, Context, Result};
use camino::Utf8Path;
use cargo_metadata::CargoOpt;
use cargo_metadata::Package;
use cargo_metadata::PackageId;
use std::collections::BTreeMap;
Expand All @@ -35,8 +36,12 @@ impl Workspaces {
// First, load information about the "omicron" workspace. This is the
// current workspace so we don't need to provide the path to it.
let ignored_non_clients = api_metadata.ignored_non_clients();
let omicron =
Arc::new(Workspace::load("omicron", None, ignored_non_clients)?);
let omicron = Arc::new(Workspace::load(
"omicron",
None,
None,
ignored_non_clients,
)?);

// In order to assemble this metadata, Cargo already has a clone of most
// of the other workspaces that we care about. We'll use those clones
Expand All @@ -55,17 +60,36 @@ impl Workspaces {
// concurrency.
let handles: Vec<_> = [
// To find this repo ... look up this package in Omicron
// v v
("crucible", "crucible-agent-client"),
("propolis", "propolis-client"),
("maghemite", "mg-admin-client"),
// | | +---- and enable these extra
// | | | features when loading
// v v v
("crucible", "crucible-agent-client", None),
(
"propolis",
"propolis-client",
// The artifacts shipped from the Propolis repo (particularly,
// `propolis-server`) are built with the `omicron-build`
// feature, which is not enabled by default. Enable this
// feature when loading the Propolis repo metadata so that we
// see the dependency tree that a shipping system will have.
Some(CargoOpt::SomeFeatures(vec![String::from(
"omicron-build",
)])),
),
("maghemite", "mg-admin-client", None),
]
.into_iter()
.map(|(repo, omicron_pkg)| {
.map(|(repo, omicron_pkg, extra_features)| {
let mine = omicron.clone();
let my_ignored = ignored_non_clients.clone();
std::thread::spawn(move || {
load_dependent_repo(&mine, repo, omicron_pkg, my_ignored)
load_dependent_repo(
&mine,
repo,
omicron_pkg,
extra_features,
my_ignored,
)
})
})
.collect();
Expand Down Expand Up @@ -97,6 +121,7 @@ impl Workspaces {
&maghemite,
"dendrite",
"dpd-client",
None,
ignored_non_clients.clone(),
)?,
);
Expand Down Expand Up @@ -231,6 +256,7 @@ fn load_dependent_repo(
workspace: &Workspace,
repo: &str,
pkgname: &str,
extra_features: Option<CargoOpt>,
ignored_non_clients: BTreeSet<ClientPackageName>,
) -> Result<Workspace> {
// `Workspace` doesn't let us look up a non-workspace package by name
Expand Down Expand Up @@ -288,5 +314,10 @@ fn load_dependent_repo(
)
})?;
let workspace_manifest = Utf8Path::new(output.trim_end());
Workspace::load(repo, Some(workspace_manifest), &ignored_non_clients)
Workspace::load(
repo,
Some(workspace_manifest),
extra_features,
&ignored_non_clients,
)
}
74 changes: 37 additions & 37 deletions dev-tools/ls-apis/tests/api_dependencies.out
Original file line number Diff line number Diff line change
@@ -1,77 +1,77 @@
Bootstrap Agent (client: bootstrap-agent-client)
consumed by: omicron-sled-agent (omicron/sled-agent)
consumed by: wicketd (omicron/wicketd)
consumed by: omicron-sled-agent (omicron/sled-agent) via 1 path
consumed by: wicketd (omicron/wicketd) via 2 paths

Clickhouse Cluster Admin (client: clickhouse-admin-client)
consumed by: omicron-nexus (omicron/nexus)
consumed by: omicron-nexus (omicron/nexus) via 3 paths

CockroachDB Cluster Admin (client: cockroach-admin-client)
consumed by: omicron-nexus (omicron/nexus)
consumed by: omicron-nexus (omicron/nexus) via 2 paths

Crucible Agent (client: crucible-agent-client)
consumed by: omicron-nexus (omicron/nexus)
consumed by: omicron-nexus (omicron/nexus) via 1 path

Crucible Control (for testing only) (client: crucible-control-client)

Crucible Pantry (client: crucible-pantry-client)
consumed by: omicron-nexus (omicron/nexus)
consumed by: omicron-nexus (omicron/nexus) via 1 path

Maghemite DDM Admin (client: ddm-admin-client)
consumed by: installinator (omicron/installinator)
consumed by: mgd (maghemite/mgd)
consumed by: omicron-sled-agent (omicron/sled-agent)
consumed by: wicketd (omicron/wicketd)
consumed by: installinator (omicron/installinator) via 1 path
consumed by: mgd (maghemite/mgd) via 1 path
consumed by: omicron-sled-agent (omicron/sled-agent) via 1 path
consumed by: wicketd (omicron/wicketd) via 1 path

DNS Server (client: dns-service-client)
consumed by: omicron-nexus (omicron/nexus)
consumed by: omicron-sled-agent (omicron/sled-agent)
consumed by: omicron-nexus (omicron/nexus) via 1 path
consumed by: omicron-sled-agent (omicron/sled-agent) via 1 path

Dendrite DPD (client: dpd-client)
consumed by: ddmd (maghemite/ddmd)
consumed by: mgd (maghemite/mgd)
consumed by: omicron-nexus (omicron/nexus)
consumed by: omicron-sled-agent (omicron/sled-agent)
consumed by: tfportd (dendrite/tfportd)
consumed by: wicketd (omicron/wicketd)
consumed by: ddmd (maghemite/ddmd) via 2 paths
consumed by: mgd (maghemite/mgd) via 1 path
consumed by: omicron-nexus (omicron/nexus) via 1 path
consumed by: omicron-sled-agent (omicron/sled-agent) via 1 path
consumed by: tfportd (dendrite/tfportd) via 1 path
consumed by: wicketd (omicron/wicketd) via 2 paths

Downstairs Controller (debugging only) (client: dsc-client)

Management Gateway Service (client: gateway-client)
consumed by: dpd (dendrite/dpd)
consumed by: omicron-nexus (omicron/nexus)
consumed by: omicron-sled-agent (omicron/sled-agent)
consumed by: wicketd (omicron/wicketd)
consumed by: dpd (dendrite/dpd) via 1 path
consumed by: omicron-nexus (omicron/nexus) via 3 paths
consumed by: omicron-sled-agent (omicron/sled-agent) via 1 path
consumed by: wicketd (omicron/wicketd) via 3 paths

Wicketd Installinator (client: installinator-client)
consumed by: installinator (omicron/installinator)
consumed by: installinator (omicron/installinator) via 1 path

Maghemite MG Admin (client: mg-admin-client)
consumed by: omicron-nexus (omicron/nexus)
consumed by: omicron-sled-agent (omicron/sled-agent)
consumed by: omicron-nexus (omicron/nexus) via 1 path
consumed by: omicron-sled-agent (omicron/sled-agent) via 1 path

Nexus Internal API (client: nexus-client)
consumed by: dpd (dendrite/dpd)
consumed by: omicron-nexus (omicron/nexus)
consumed by: omicron-sled-agent (omicron/sled-agent)
consumed by: oximeter-collector (omicron/oximeter/collector)
consumed by: propolis-server (propolis/bin/propolis-server)
consumed by: dpd (dendrite/dpd) via 1 path
consumed by: omicron-nexus (omicron/nexus) via 1 path
consumed by: omicron-sled-agent (omicron/sled-agent) via 1 path
consumed by: oximeter-collector (omicron/oximeter/collector) via 1 path
consumed by: propolis-server (propolis/bin/propolis-server) via 3 paths

External API (client: oxide-client)

Oximeter (client: oximeter-client)
consumed by: omicron-nexus (omicron/nexus)
consumed by: omicron-nexus (omicron/nexus) via 2 paths

Propolis (client: propolis-client)
consumed by: omicron-nexus (omicron/nexus)
consumed by: omicron-sled-agent (omicron/sled-agent)
consumed by: omicron-nexus (omicron/nexus) via 2 paths
consumed by: omicron-sled-agent (omicron/sled-agent) via 1 path

Crucible Repair (client: repair-client)
consumed by: crucible-downstairs (crucible/downstairs)
consumed by: crucible-downstairs (crucible/downstairs) via 1 path

Sled Agent (client: sled-agent-client)
consumed by: dpd (dendrite/dpd)
consumed by: omicron-nexus (omicron/nexus)
consumed by: omicron-sled-agent (omicron/sled-agent)
consumed by: dpd (dendrite/dpd) via 1 path
consumed by: omicron-nexus (omicron/nexus) via 7 paths
consumed by: omicron-sled-agent (omicron/sled-agent) via 1 path

Wicketd (client: wicketd-client)

Loading