Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 564ce96

Browse files
committedMar 20, 2025··
[ls-apis] support APIs exposed by multiple servers
Currently, `cargo xtask ls-apis` will fail when it encounters an API that is served by multiple entities. For example, when adding the `ereporter-api` in #7833, which is served by both MGS and sled-agent, `ls-apis` currently fails with an error like this one: ```console eliza@theseus ~/Code/oxide/omicron $ cargo xtask ls-apis apis Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.45s Running `target/debug/xtask ls-apis apis` Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.01s Running `target/debug/ls-apis apis` loading metadata for workspace omicron from current workspace loading metadata for workspace lldp from /home/eliza/.cargo/git/checkouts/lldp-d47de417041f191b/ce952e6/Cargo.toml loading metadata for workspace propolis from /home/eliza/.cargo/git/checkouts/propolis-d68c8bd1bc59c9bd/6b5f2af/Cargo.toml loading metadata for workspace crucible from /home/eliza/.cargo/git/checkouts/crucible-0a48bd218bc2bbbc/81a3528/Cargo.toml loading metadata for workspace maghemite from /home/eliza/.cargo/git/checkouts/maghemite-c0236f0fd3d582b6/caafd88/Cargo.toml loading metadata for workspace dendrite from /home/eliza/.cargo/git/checkouts/dendrite-ae9f1715c17fc765/a66561e/Cargo.toml note: ignoring Cargo dependency from crucible-pantry -> ... -> crucible-control-client note: ignoring Cargo dependency from omicron-sled-agent -> dns-server error: API for client ereporter-client appears to be exported by multiple components: at least omicron-sled-agent and omicron-gateway (DepPath([PackageId { repr: "path+file:///home/eliza/Code/oxide/omicron/gateway#omicron-gateway@0.1.0" }])) Error: found at least one API exported by multiple servers # ... backtrace snipped for brevity ... ``` This is unfortunate, as it would be nice to be able to have such APIs without breaking `ls-apis`. Therefore, this commit adds support for APIs with multiple server components. This change ends up being pretty straightforward; it's mainly just changing the `api_producers` map from a map of client package names to server components to a map of client package names to *a map of* server components, and updating the code that consumes this information to loop over those server components as appropriate. Now, the various `ls-apis` commands can handle the new `ereproter-api`. For example: ```console eliza@theseus ~/Code/oxide/omicron $ cargo xtask ls-apis apis Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.37s Running `target/debug/xtask ls-apis apis` Compiling omicron-ls-apis v0.1.0 (/home/eliza/Code/oxide/omicron/dev-tools/ls-apis) Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.69s Running `target/debug/ls-apis apis` loading metadata for workspace omicron from current workspace loading metadata for workspace lldp from /home/eliza/.cargo/git/checkouts/lldp-d47de417041f191b/ce952e6/Cargo.toml loading metadata for workspace maghemite from /home/eliza/.cargo/git/checkouts/maghemite-c0236f0fd3d582b6/caafd88/Cargo.toml loading metadata for workspace propolis from /home/eliza/.cargo/git/checkouts/propolis-d68c8bd1bc59c9bd/6b5f2af/Cargo.toml loading metadata for workspace crucible from /home/eliza/.cargo/git/checkouts/crucible-0a48bd218bc2bbbc/81a3528/Cargo.toml loading metadata for workspace dendrite from /home/eliza/.cargo/git/checkouts/dendrite-ae9f1715c17fc765/a66561e/Cargo.toml note: ignoring Cargo dependency from crucible-pantry -> ... -> crucible-control-client note: ignoring Cargo dependency from omicron-sled-agent -> dns-server Bootstrap Agent (client: bootstrap-agent-client) consumed by: omicron-sled-agent (omicron/sled-agent) via 1 path consumed by: wicketd (omicron/wicketd) via 2 paths # ... snipped irrelevant APIs for brevity ... Ereporter (client: ereporter-client) consumed by: omicron-nexus (omicron/nexus) via 1 path # ... snipped irrelevant APIs for brevity ... eliza@theseus ~/Code/oxide/omicron $ cargo xtask ls-apis check Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.32s Running `target/debug/xtask ls-apis check` Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.52s Running `target/debug/ls-apis check` loading metadata for workspace omicron from current workspace loading metadata for workspace lldp from /home/eliza/.cargo/git/checkouts/lldp-d47de417041f191b/ce952e6/Cargo.toml loading metadata for workspace maghemite from /home/eliza/.cargo/git/checkouts/maghemite-c0236f0fd3d582b6/caafd88/Cargo.toml loading metadata for workspace propolis from /home/eliza/.cargo/git/checkouts/propolis-d68c8bd1bc59c9bd/6b5f2af/Cargo.toml loading metadata for workspace crucible from /home/eliza/.cargo/git/checkouts/crucible-0a48bd218bc2bbbc/81a3528/Cargo.toml loading metadata for workspace dendrite from /home/eliza/.cargo/git/checkouts/dendrite-ae9f1715c17fc765/a66561e/Cargo.toml note: ignoring Cargo dependency from crucible-pantry -> ... -> crucible-control-client note: ignoring Cargo dependency from omicron-sled-agent -> dns-server Server-managed APIs: Clickhouse Cluster Admin for Keepers (clickhouse-admin-keeper-client, exposed by omicron-clickhouse-admin) Clickhouse Cluster Admin for Servers (clickhouse-admin-server-client, exposed by omicron-clickhouse-admin) Clickhouse Single-Node Cluster Admin (clickhouse-admin-single-client, exposed by omicron-clickhouse-admin) CockroachDB Cluster Admin (cockroach-admin-client, exposed by omicron-cockroach-admin) Crucible Agent (crucible-agent-client, exposed by crucible-agent) Crucible Control (for testing only) (crucible-control-client, exposed by propolis-server) Crucible Pantry (crucible-pantry-client, exposed by crucible-pantry) Maghemite DDM Admin (ddm-admin-client, exposed by ddmd) DNS Server (dns-service-client, exposed by dns-server) Ereporter (ereporter-client, exposed by omicron-gateway, omicron-sled-agent) Management Gateway Service (gateway-client, exposed by omicron-gateway) LLDP daemon (lldpd-client, exposed by lldpd) Maghemite MG Admin (mg-admin-client, exposed by mgd) External API (oxide-client, exposed by omicron-nexus) Oximeter (oximeter-client, exposed by oximeter-collector) Propolis (propolis-client, exposed by propolis-server) Sled Agent (sled-agent-client, exposed by omicron-sled-agent) Wicketd (wicketd-client, exposed by wicketd) Client-managed API: Bootstrap Agent (bootstrap-agent-client, exposed by omicron-sled-agent) reason: depends on itself (i.e., instances call each other) Dendrite DPD (dpd-client, exposed by dpd) reason: Sled Agent calling DPD creates a circular dependency Wicketd Installinator (installinator-client, exposed by wicketd) reason: client is provided implicitly by the operator Nexus Internal API (nexus-client, exposed by omicron-nexus) reason: Circular dependencies between Nexus and other services Crucible Repair (repair-client, exposed by crucible-downstairs) reason: depends on itself (i.e., instances call each other) Repo Depot API (repo-depot-client, exposed by omicron-sled-agent) reason: depends on itself (i.e., instances call each other) APIs with unknown version management: none ````
1 parent 592e8b0 commit 564ce96

File tree

3 files changed

+202
-173
lines changed

3 files changed

+202
-173
lines changed
 

‎dev-tools/ls-apis/src/bin/ls-apis.rs

+46-52
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use anyhow::{Context, Result, bail};
88
use camino::Utf8PathBuf;
99
use clap::{Args, Parser, Subcommand};
1010
use omicron_ls_apis::{
11-
AllApiMetadata, ApiDependencyFilter, LoadArgs, ServerComponentName,
12-
SystemApis, VersionedHow,
11+
AllApiMetadata, ApiDependencyFilter, ApiMetadata, LoadArgs,
12+
ServerComponentName, SystemApis, VersionedHow,
1313
};
1414
use parse_display::{Display, FromStr};
1515

@@ -108,34 +108,33 @@ fn run_adoc(apis: &SystemApis) -> Result<()> {
108108

109109
let metadata = apis.api_metadata();
110110
for api in metadata.apis() {
111-
let Some(server_component) =
112-
apis.api_producer(&api.client_package_name)
113-
else {
114-
continue;
115-
};
116-
println!("// DO NOT EDIT. This table is auto-generated. See above.");
117-
println!("|{}", api.label);
118-
println!("|{}", apis.adoc_label(server_component)?);
119-
println!("|{}", apis.adoc_label(&api.client_package_name)?);
120-
println!("|");
121-
122-
for (c, _) in apis.api_consumers(
123-
&api.client_package_name,
124-
ApiDependencyFilter::default(),
125-
)? {
126-
println!("* {}", apis.adoc_label(c)?);
127-
}
128-
129-
match &api.versioned_how {
130-
VersionedHow::Unknown => println!("|TBD"),
131-
VersionedHow::Server => println!("|Server-side only"),
132-
VersionedHow::Client(reason) => {
133-
println!("|Client-side ({})", reason);
111+
for server_component in apis.api_producers(&api.client_package_name) {
112+
println!(
113+
"// DO NOT EDIT. This table is auto-generated. See above."
114+
);
115+
println!("|{}", api.label);
116+
println!("|{}", apis.adoc_label(server_component)?);
117+
println!("|{}", apis.adoc_label(&api.client_package_name)?);
118+
println!("|");
119+
120+
for (c, _) in apis.api_consumers(
121+
&api.client_package_name,
122+
ApiDependencyFilter::default(),
123+
)? {
124+
println!("* {}", apis.adoc_label(c)?);
134125
}
135-
};
136126

137-
print!("|{}", api.notes.as_deref().unwrap_or("-\n"));
138-
println!("");
127+
match &api.versioned_how {
128+
VersionedHow::Unknown => println!("|TBD"),
129+
VersionedHow::Server => println!("|Server-side only"),
130+
VersionedHow::Client(reason) => {
131+
println!("|Client-side ({})", reason);
132+
}
133+
};
134+
135+
print!("|{}", api.notes.as_deref().unwrap_or("-\n"));
136+
println!("");
137+
}
139138
}
140139

141140
println!("|===\n");
@@ -214,12 +213,10 @@ fn print_server_components<'a>(
214213
for s in server_components.into_iter() {
215214
let (repo_name, pkg_path) = apis.package_label(s)?;
216215
println!("{}{} ({}/{})", prefix, s, repo_name, pkg_path);
217-
for api in metadata.apis().filter(|a| {
218-
matches!(
219-
apis.api_producer(&a.client_package_name),
220-
Some (name) if name == s
221-
)
222-
}) {
216+
for api in metadata
217+
.apis()
218+
.filter(|a| apis.is_producer_of(s, &a.client_package_name))
219+
{
223220
println!(
224221
"{} exposes: {} (client = {})",
225222
prefix, api.label, api.client_package_name
@@ -301,31 +298,33 @@ fn run_check(apis: &SystemApis) -> Result<()> {
301298
);
302299
}
303300

301+
fn print_api_and_producers(api: &ApiMetadata, apis: &SystemApis) {
302+
print!(" {} ({}", api.label, api.client_package_name,);
303+
let mut producers = apis.api_producers(&api.client_package_name);
304+
if let Some(producer) = producers.next() {
305+
print!(", exposed by {producer}");
306+
for producer in producers {
307+
print!(", {producer}")
308+
}
309+
}
310+
println!(")");
311+
}
312+
304313
println!("\n");
305314
println!("Server-managed APIs:\n");
306315
for api in apis
307316
.api_metadata()
308317
.apis()
309318
.filter(|f| f.deployed() && f.versioned_how == VersionedHow::Server)
310319
{
311-
println!(
312-
" {} ({}, exposed by {})",
313-
api.label,
314-
api.client_package_name,
315-
apis.api_producer(&api.client_package_name).unwrap()
316-
);
320+
print_api_and_producers(api, apis);
317321
}
318322

319323
println!("\n");
320324
println!("Client-managed API:\n");
321325
for api in apis.api_metadata().apis().filter(|f| f.deployed()) {
322326
if let VersionedHow::Client(reason) = &api.versioned_how {
323-
println!(
324-
" {} ({}, exposed by {})",
325-
api.label,
326-
api.client_package_name,
327-
apis.api_producer(&api.client_package_name).unwrap()
328-
);
327+
print_api_and_producers(api, apis);
329328
println!(" reason: {}", reason);
330329
}
331330
}
@@ -342,12 +341,7 @@ fn run_check(apis: &SystemApis) -> Result<()> {
342341
} else {
343342
println!("\n");
344343
for api in unknown {
345-
println!(
346-
" {} ({}, exposed by {})",
347-
api.label,
348-
api.client_package_name,
349-
apis.api_producer(&api.client_package_name).unwrap()
350-
);
344+
print_api_and_producers(api, apis);
351345
}
352346
bail!("at least one API has unknown version strategy (see above)");
353347
}

‎dev-tools/ls-apis/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ mod system_apis;
1010
mod workspaces;
1111

1212
pub use api_metadata::AllApiMetadata;
13+
pub use api_metadata::ApiMetadata;
1314
pub use api_metadata::VersionedHow;
1415
pub use system_apis::ApiDependencyFilter;
1516
pub use system_apis::SystemApis;

0 commit comments

Comments
 (0)
Please sign in to comment.