diff --git a/Cargo.lock b/Cargo.lock index 15fdd78e44..711cc2f387 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5238,7 +5238,9 @@ dependencies = [ "dropshot", "http 0.2.12", "illumos-utils", + "nexus-test-utils", "omicron-common", + "omicron-test-utils", "omicron-workspace-hack", "schemars", "serde", @@ -5249,6 +5251,7 @@ dependencies = [ "thiserror", "tokio", "toml 0.8.13", + "url", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index fbb13e26c7..e6b0ffb099 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -486,6 +486,7 @@ typed-rng = { path = "typed-rng" } unicode-width = "0.1.11" update-common = { path = "update-common" } update-engine = { path = "update-engine" } +url = "2.5.0" usdt = "0.5.0" uuid = { version = "1.8.0", features = ["serde", "v4"] } uzers = "0.11" diff --git a/cockroach-admin/Cargo.toml b/cockroach-admin/Cargo.toml index 39571aad51..149e5844f2 100644 --- a/cockroach-admin/Cargo.toml +++ b/cockroach-admin/Cargo.toml @@ -26,5 +26,10 @@ toml.workspace = true omicron-workspace-hack.workspace = true +[dev-dependencies] +nexus-test-utils.workspace = true +omicron-test-utils.workspace = true +url.workspace = true + [lints] workspace = true diff --git a/cockroach-admin/src/cockroach_cli.rs b/cockroach-admin/src/cockroach_cli.rs index ce7a4c6665..a5c416a197 100644 --- a/cockroach-admin/src/cockroach_cli.rs +++ b/cockroach-admin/src/cockroach_cli.rs @@ -201,9 +201,11 @@ impl NodeStatus { #[cfg(test)] mod tests { - use chrono::NaiveDate; - use super::*; + use chrono::NaiveDate; + use nexus_test_utils::db::test_setup_database; + use omicron_test_utils::dev; + use url::Url; #[test] fn test_node_status_parse_single_line_from_csv() { @@ -371,4 +373,64 @@ mod tests { assert_eq!(status, expected); } } + + // Ensure that if `cockroach node status` changes in a future CRDB version + // bump, we have a test that will fail to force us to check whether our + // current parsing is still valid. + #[tokio::test] + async fn test_node_status_compatibility() { + let logctx = dev::test_setup_log( + "test_project_create_vpc_raw_returns_none_on_vni_exhaustion", + ); + let mut db = test_setup_database(&logctx.log).await; + let db_url = db.listen_url().to_string(); + + let expected_headers = "id,address,sql_address,build,started_at,updated_at,locality,is_available,is_live"; + + // Manually run cockroach node status to grab just the CSV header line + // (which the `csv` crate normally eats on our behalf) and check it's + // exactly what we expect. + let mut command = Command::new("cockroach"); + command + .arg("node") + .arg("status") + .arg("--url") + .arg(&db_url) + .arg("--format") + .arg("csv"); + let output = + command.output().await.expect("ran `cockroach node status`"); + + let stdout = String::from_utf8_lossy(&output.stdout); + let mut lines = stdout.lines(); + let headers = lines.next().expect("header line"); + assert_eq!( + headers, expected_headers, + "`cockroach node status --format csv` headers may have changed?" + ); + + // We should also be able to run our wrapper against this cockroach. + let url: Url = db_url.parse().expect("valid url"); + let cockroach_address: SocketAddrV6 = format!( + "{}:{}", + url.host().expect("url has host"), + url.port().expect("url has port") + ) + .parse() + .expect("valid SocketAddrV6"); + let cli = CockroachCli::new("cockroach".into(), cockroach_address); + let status = cli.node_status().await.expect("got node status"); + + // We can't check all the fields exactly, but some we know based on the + // fact that our test database is a single node. + assert_eq!(status.len(), 1); + assert_eq!(status[0].node_id, "1"); + assert_eq!(status[0].address, SocketAddr::V6(cockroach_address)); + assert_eq!(status[0].sql_address, SocketAddr::V6(cockroach_address)); + assert_eq!(status[0].is_available, true); + assert_eq!(status[0].is_live, true); + + db.cleanup().await.unwrap(); + logctx.cleanup_successful(); + } } diff --git a/tufaceous-lib/Cargo.toml b/tufaceous-lib/Cargo.toml index e448ed6db5..61224e6080 100644 --- a/tufaceous-lib/Cargo.toml +++ b/tufaceous-lib/Cargo.toml @@ -36,7 +36,7 @@ tar.workspace = true tokio.workspace = true toml.workspace = true tough.workspace = true -url = "2.5.0" +url.workspace = true zip.workspace = true omicron-workspace-hack.workspace = true