Skip to content

Commit c3a2245

Browse files
committed
Merge
2 parents 156ca0c + b7ba37f commit c3a2245

File tree

68 files changed

+10135
-8862
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+10135
-8862
lines changed

Cargo.lock

Lines changed: 208 additions & 186 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ members = [
155155
"wicketd",
156156
"wicketd-api",
157157
"workspace-hack",
158-
"zone-setup",
158+
"zone-setup"
159159
]
160160

161161
default-members = [
@@ -441,9 +441,9 @@ dns-server = { path = "dns-server" }
441441
dns-server-api = { path = "dns-server-api" }
442442
dns-service-client = { path = "clients/dns-service-client" }
443443
dpd-client = { git = "https://github.com/oxidecomputer/dendrite", rev = "cc8e02a0800034c431c8cf96b889ea638da3d194" }
444-
dropshot = { version = "0.16.3", features = [ "usdt-probes" ] }
445-
dropshot-api-manager = "0.2.3"
446-
dropshot-api-manager-types = "0.2.3"
444+
dropshot = { version = "0.16.5", features = [ "usdt-probes" ] }
445+
dropshot-api-manager = "0.2.4"
446+
dropshot-api-manager-types = "0.2.4"
447447
dyn-clone = "1.0.20"
448448
either = "1.15.0"
449449
ereport-types = { path = "ereport/types" }
@@ -573,7 +573,7 @@ nexus-test-interface = { path = "nexus/test-interface" }
573573
nexus-test-utils-macros = { path = "nexus/test-utils-macros" }
574574
nexus-test-utils = { path = "nexus/test-utils" }
575575
nexus-types = { path = "nexus/types" }
576-
nix = { version = "0.30", features = ["net"] }
576+
nix = { version = "0.30", features = ["fs", "net"] }
577577
nom = "7.1.3"
578578
num-integer = "0.1.46"
579579
num = { version = "0.4.3", default-features = false, features = [ "libm" ] }
@@ -755,7 +755,7 @@ termtree = "0.5.1"
755755
textwrap = { version = "0.16.2", features = [ "terminal_size" ] }
756756
test-strategy = "0.4.3"
757757
thiserror = "2.0"
758-
tofino = { git = "https://github.com/oxidecomputer/tofino", branch = "main" }
758+
tofino = { git = "https://github.com/oxidecomputer/tofino" }
759759
tokio = "1.47.0"
760760
tokio-postgres = { version = "0.7", features = [ "with-chrono-0_4", "with-uuid-1" ] }
761761
tokio-stream = "0.1.17"

clients/oxide-client/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use std::sync::Arc;
1717
use thiserror::Error;
1818

1919
progenitor::generate_api!(
20-
spec = "../../openapi/nexus.json",
20+
spec = "../../openapi/nexus/nexus-latest.json",
2121
interface = Builder,
2222
tags = Separate,
2323
);

dev-tools/dropshot-apis/src/main.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,9 @@ fn all_apis() -> anyhow::Result<ManagedApis> {
192192
},
193193
ManagedApiConfig {
194194
title: "Oxide Region API",
195-
versions: Versions::new_lockstep(semver::Version::new(
196-
20251208, 0, 0,
197-
)),
195+
versions: Versions::new_versioned(
196+
nexus_external_api::supported_versions(),
197+
),
198198
metadata: ManagedApiMetadata {
199199
description: Some(
200200
"API for interacting with the Oxide control plane",

dev-tools/omdb/src/bin/omdb/db.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ mod ereport;
183183
mod saga;
184184
mod sitrep;
185185
mod user_data_export;
186+
mod whatis;
186187

187188
const NO_ACTIVE_PROPOLIS_MSG: &str = "<no active Propolis>";
188189
const NOT_ON_SLED_MSG: &str = "<not on any sled>";
@@ -417,6 +418,11 @@ enum DbCommands {
417418
Zpool(ZpoolArgs),
418419
/// Commands for querying and interacting with user data export objects
419420
UserDataExport(user_data_export::UserDataExportArgs),
421+
/// Given a UUID, try to figure out what type of object it refers to
422+
///
423+
/// More precisely, `omdb db whatis` reports tables containing a unique UUID
424+
/// column with the specified value.
425+
Whatis(whatis::WhatisArgs),
420426
}
421427

422428
#[derive(Debug, Args, Clone)]
@@ -1489,6 +1495,9 @@ impl DbArgs {
14891495
DbCommands::UserDataExport(args) => {
14901496
args.exec(&omdb, &opctx, &datastore).await
14911497
}
1498+
DbCommands::Whatis(args) => {
1499+
whatis::cmd_db_whatis(&datastore, args).await
1500+
}
14921501
}
14931502
}
14941503
}).await
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
//! `omdb db whatis` subcommand
6+
//!
7+
//! Heuristically determine what type of object a given UUID refers to
8+
9+
use anyhow::Context;
10+
use async_bb8_diesel::AsyncRunQueryDsl;
11+
use clap::Args;
12+
use nexus_db_queries::db::DataStore;
13+
use uuid::Uuid;
14+
15+
#[derive(Debug, Args, Clone)]
16+
pub(super) struct WhatisArgs {
17+
/// The UUID(s) to look up
18+
uuids: Vec<Uuid>,
19+
20+
/// Show all tables that were checked
21+
#[clap(long)]
22+
debug: bool,
23+
}
24+
25+
// `omdb db whatis UUID...` heuristically determines what type of object a UUID
26+
// refers to by first searching the database for unique UUID columns and then
27+
// searching those tables for a matching row.
28+
pub(super) async fn cmd_db_whatis(
29+
datastore: &DataStore,
30+
args: &WhatisArgs,
31+
) -> Result<(), anyhow::Error> {
32+
let conn = datastore.pool_connection_for_tests().await?;
33+
34+
// Query CockroachDB's information schema to find UUID columns that are the
35+
// sole column in a unique constraint or primary key with no associated
36+
// WHERE clause (i.e., not a partial index) . This ensures we only query
37+
// columns that have their own index (avoiding table scans).
38+
//
39+
// We use this approach rather than hardcoding all the kinds of objects that
40+
// we know about so that this stays up-to-date with other changes to the
41+
// system.
42+
let query = "
43+
WITH constraint_column_counts AS (
44+
SELECT
45+
constraint_catalog,
46+
constraint_schema,
47+
constraint_name,
48+
table_catalog,
49+
table_schema,
50+
table_name,
51+
COUNT(*) as column_count
52+
FROM
53+
information_schema.key_column_usage
54+
GROUP BY
55+
constraint_catalog,
56+
constraint_schema,
57+
constraint_name,
58+
table_catalog,
59+
table_schema,
60+
table_name
61+
)
62+
SELECT DISTINCT
63+
tc.table_name,
64+
kcu.column_name
65+
FROM
66+
information_schema.table_constraints tc
67+
JOIN information_schema.key_column_usage kcu
68+
ON tc.constraint_catalog = kcu.constraint_catalog
69+
AND tc.constraint_schema = kcu.constraint_schema
70+
AND tc.constraint_name = kcu.constraint_name
71+
AND tc.table_catalog = kcu.table_catalog
72+
AND tc.table_schema = kcu.table_schema
73+
AND tc.table_name = kcu.table_name
74+
JOIN information_schema.columns c
75+
ON kcu.table_catalog = c.table_catalog
76+
AND kcu.table_schema = c.table_schema
77+
AND kcu.table_name = c.table_name
78+
AND kcu.column_name = c.column_name
79+
JOIN constraint_column_counts ccc
80+
ON tc.constraint_catalog = ccc.constraint_catalog
81+
AND tc.constraint_schema = ccc.constraint_schema
82+
AND tc.constraint_name = ccc.constraint_name
83+
AND tc.table_catalog = ccc.table_catalog
84+
AND tc.table_schema = ccc.table_schema
85+
AND tc.table_name = ccc.table_name
86+
LEFT JOIN pg_indexes idx
87+
ON idx.tablename = tc.table_name
88+
AND idx.indexname = tc.constraint_name
89+
AND idx.schemaname = tc.table_schema
90+
WHERE
91+
tc.constraint_type IN ('PRIMARY KEY', 'UNIQUE')
92+
AND tc.table_schema = 'public'
93+
AND c.data_type = 'uuid'
94+
AND ccc.column_count = 1
95+
AND (idx.indexdef IS NULL OR idx.indexdef NOT LIKE '% WHERE %')
96+
ORDER BY
97+
tc.table_name, kcu.column_name
98+
";
99+
100+
#[derive(Debug, diesel::QueryableByName)]
101+
struct UniqueUuidColumn {
102+
#[diesel(sql_type = diesel::sql_types::Text)]
103+
table_name: String,
104+
#[diesel(sql_type = diesel::sql_types::Text)]
105+
column_name: String,
106+
}
107+
108+
let unique_columns: Vec<UniqueUuidColumn> = diesel::sql_query(query)
109+
.load_async(&*conn)
110+
.await
111+
.context("querying information_schema for unique UUID columns")?;
112+
113+
// Search separately for each UUID provided on the command line.
114+
for &uuid in &args.uuids {
115+
let mut found = false;
116+
117+
// For each unique UUID column that we found above, see if there's a row
118+
// in the corresponding table for the given UUID value.
119+
for col in &unique_columns {
120+
let check_query = format!(
121+
"SELECT EXISTS(SELECT 1 FROM {} WHERE {} = $1) as exists",
122+
col.table_name, col.column_name
123+
);
124+
125+
let table_column =
126+
format!("{}.{}", col.table_name, col.column_name);
127+
if args.debug {
128+
eprintln!("checking table {table_column:?}");
129+
}
130+
131+
// `sql_query()` requires extracting the results with a struct.
132+
#[derive(Debug, diesel::QueryableByName)]
133+
struct ExistsResult {
134+
#[diesel(sql_type = diesel::sql_types::Bool)]
135+
exists: bool,
136+
}
137+
138+
let exists_result: ExistsResult = diesel::sql_query(&check_query)
139+
.bind::<diesel::sql_types::Uuid, _>(uuid)
140+
.get_result_async(&*conn)
141+
.await
142+
.with_context(|| {
143+
format!("checking {table_column} for UUID {uuid}")
144+
})?;
145+
146+
if exists_result.exists {
147+
println!("{uuid} found in {table_column}");
148+
found = true;
149+
}
150+
}
151+
152+
if !found {
153+
println!("{uuid} not found in any unique UUID column");
154+
}
155+
}
156+
157+
Ok(())
158+
}

dev-tools/omdb/src/bin/omdb/nexus.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ use nexus_types::internal_api::background::SitrepGcStatus;
7272
use nexus_types::internal_api::background::SitrepLoadStatus;
7373
use nexus_types::internal_api::background::SupportBundleCleanupReport;
7474
use nexus_types::internal_api::background::SupportBundleCollectionReport;
75+
use nexus_types::internal_api::background::SupportBundleCollectionStepStatus;
7576
use nexus_types::internal_api::background::SupportBundleEreportStatus;
7677
use nexus_types::internal_api::background::TufArtifactReplicationCounters;
7778
use nexus_types::internal_api::background::TufArtifactReplicationRequest;
@@ -2622,9 +2623,9 @@ fn print_task_support_bundle_collector(details: &serde_json::Value) {
26222623
#[tabled(rename_all = "SCREAMING_SNAKE_CASE")]
26232624
struct StepRow {
26242625
step_name: String,
2625-
start_time: String,
2626+
start_time: DateTime<Utc>,
26262627
duration: String,
2627-
status: String,
2628+
status: SupportBundleCollectionStepStatus,
26282629
}
26292630

26302631
steps.sort_unstable_by_key(|s| s.start);
@@ -2636,9 +2637,9 @@ fn print_task_support_bundle_collector(details: &serde_json::Value) {
26362637
.unwrap_or(Duration::from_millis(0));
26372638
StepRow {
26382639
step_name: step.name,
2639-
start_time: step.start.to_rfc3339(),
2640+
start_time: step.start,
26402641
duration: format!("{:.3}s", duration.as_secs_f64()),
2641-
status: step.status.to_string(),
2642+
status: step.status,
26422643
}
26432644
})
26442645
.collect();

dev-tools/omdb/tests/successes.out

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1975,6 +1975,20 @@ note: database schema version matches expected (<redacted database version>)
19751975
assembling reconfigurator state ... done
19761976
saving to <TMP_PATH_REDACTED> ... done
19771977
=============================================
1978+
EXECUTING COMMAND: omdb ["db", "whatis", "..........<REDACTED_UUID>...........", "..........<REDACTED_UUID>..........."]
1979+
termination: Exited(0)
1980+
---------------------------------------------
1981+
stdout:
1982+
..........<REDACTED_UUID>........... found in silo.id
1983+
..........<REDACTED_UUID>........... found in silo_auth_settings.silo_id
1984+
..........<REDACTED_UUID>........... found in silo_quotas.silo_id
1985+
..........<REDACTED_UUID>........... found in virtual_provisioning_collection.id
1986+
..........<REDACTED_UUID>........... found in silo_user.id
1987+
---------------------------------------------
1988+
stderr:
1989+
note: using database URL postgresql://root@[::1]:REDACTED_PORT/omicron?sslmode=disable
1990+
note: database schema version matches expected (<redacted database version>)
1991+
=============================================
19781992
EXECUTING COMMAND: omdb ["--destructive", "db", "db-metadata", "force-mark-nexus-quiesced", "--skip-confirmation", "--skip-blueprint-validation", "..........<REDACTED_UUID>..........."]
19791993
termination: Exited(0)
19801994
---------------------------------------------

dev-tools/omdb/tests/test_all_output.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,13 @@ async fn test_omdb_success_cases(cptestctx: &ControlPlaneTestContext) {
292292
// provided by a real sled agent, which is not available in the
293293
// ControlPlaneTestContext.
294294

295+
// Test the whatis command with two known UUIDs
296+
&[
297+
"db",
298+
"whatis",
299+
"001de000-5110-4000-8000-000000000000",
300+
"001de000-05e4-4000-8000-000000004007",
301+
],
295302
// This operation will set the "db_metadata_nexus" state to quiesced.
296303
//
297304
// This would normally only be set by a Nexus as it shuts itself down;

dev-tools/omdb/tests/usage_errors.out

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ Commands:
150150
alert Print information about alerts
151151
zpool Commands for querying and interacting with pools
152152
user-data-export Commands for querying and interacting with user data export objects
153+
whatis Given a UUID, try to figure out what type of object it refers to
153154
help Print this message or the help of the given subcommand(s)
154155

155156
Options:
@@ -214,6 +215,7 @@ Commands:
214215
alert Print information about alerts
215216
zpool Commands for querying and interacting with pools
216217
user-data-export Commands for querying and interacting with user data export objects
218+
whatis Given a UUID, try to figure out what type of object it refers to
217219
help Print this message or the help of the given subcommand(s)
218220

219221
Options:

0 commit comments

Comments
 (0)