Skip to content

Commit

Permalink
Add internal API and omdb command to expunge a sled (#5234)
Browse files Browse the repository at this point in the history
The omdb interface might be overly cautious; feedback welcome. You can
try this against `omicron-dev run-all`; it looks roughly like:

```
note: using Nexus URL http://[::1]:12221
note: using database URL postgresql://root@[::1]:42285/omicron?sslmode=disable
note: database schema version matches expected (39.0.0)
WARNING: sled b6d65341-167c-41df-9b5c-41cded99c229 IS PRESENT in the most recent inventory collection; are you sure you want to mark it expunged?
WARNING: This operation will PERMANENTLY and IRRECOVABLY mark sled b6d65341-167c-41df-9b5c-41cded99c229 (sim-b6d65341) expunged. To proceed, type the sled's serial number.
sled serial〉sim-b6d65341
expunged sled b6d65341-167c-41df-9b5c-41cded99c229 (previous policy: InService(Provisionable))
```

This handles the "internal" half of #5134
  • Loading branch information
jgallagher authored Mar 13, 2024
1 parent 13a28f0 commit 4f101be
Show file tree
Hide file tree
Showing 10 changed files with 413 additions and 63 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dev-tools/omdb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ oximeter-client.workspace = true
# See omicron-rpaths for more about the "pq-sys" dependency.
pq-sys = "*"
ratatui.workspace = true
reedline.workspace = true
serde.workspace = true
serde_json.workspace = true
sled-agent-client.workspace = true
Expand Down
112 changes: 68 additions & 44 deletions dev-tools/omdb/src/bin/omdb/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,8 @@ impl Display for MaybeSledId {

#[derive(Debug, Args)]
pub struct DbArgs {
/// URL of the database SQL interface
#[clap(long, env("OMDB_DB_URL"))]
db_url: Option<PostgresConfigWithUrl>,
#[clap(flatten)]
db_url_opts: DbUrlOptions,

#[clap(flatten)]
fetch_opts: DbFetchOptions,
Expand All @@ -159,6 +158,71 @@ pub struct DbArgs {
command: DbCommands,
}

#[derive(Debug, Args)]
pub struct DbUrlOptions {
/// URL of the database SQL interface
#[clap(long, env("OMDB_DB_URL"))]
db_url: Option<PostgresConfigWithUrl>,
}

impl DbUrlOptions {
async fn resolve_pg_url(
&self,
omdb: &Omdb,
log: &slog::Logger,
) -> anyhow::Result<PostgresConfigWithUrl> {
match &self.db_url {
Some(cli_or_env_url) => Ok(cli_or_env_url.clone()),
None => {
eprintln!(
"note: database URL not specified. Will search DNS."
);
eprintln!("note: (override with --db-url or OMDB_DB_URL)");
let addrs = omdb
.dns_lookup_all(
log.clone(),
internal_dns::ServiceName::Cockroach,
)
.await?;

format!(
"postgresql://root@{}/omicron?sslmode=disable",
addrs
.into_iter()
.map(|a| a.to_string())
.collect::<Vec<_>>()
.join(",")
)
.parse()
.context("failed to parse constructed postgres URL")
}
}
}

pub async fn connect(
&self,
omdb: &Omdb,
log: &slog::Logger,
) -> anyhow::Result<Arc<DataStore>> {
let db_url = self.resolve_pg_url(omdb, log).await?;
eprintln!("note: using database URL {}", &db_url);

let db_config = db::Config { url: db_url.clone() };
let pool = Arc::new(db::Pool::new(&log.clone(), &db_config));

// Being a dev tool, we want to try this operation even if the schema
// doesn't match what we expect. So we use `DataStore::new_unchecked()`
// here. We will then check the schema version explicitly and warn the
// user if it doesn't match.
let datastore = Arc::new(
DataStore::new_unchecked(log.clone(), pool)
.map_err(|e| anyhow!(e).context("creating datastore"))?,
);
check_schema_version(&datastore).await;
Ok(datastore)
}
}

#[derive(Debug, Args)]
pub struct DbFetchOptions {
/// limit to apply to queries that fetch rows
Expand Down Expand Up @@ -405,47 +469,7 @@ impl DbArgs {
omdb: &Omdb,
log: &slog::Logger,
) -> Result<(), anyhow::Error> {
let db_url = match &self.db_url {
Some(cli_or_env_url) => cli_or_env_url.clone(),
None => {
eprintln!(
"note: database URL not specified. Will search DNS."
);
eprintln!("note: (override with --db-url or OMDB_DB_URL)");
let addrs = omdb
.dns_lookup_all(
log.clone(),
internal_dns::ServiceName::Cockroach,
)
.await?;

format!(
"postgresql://root@{}/omicron?sslmode=disable",
addrs
.into_iter()
.map(|a| a.to_string())
.collect::<Vec<_>>()
.join(",")
)
.parse()
.context("failed to parse constructed postgres URL")?
}
};
eprintln!("note: using database URL {}", &db_url);

let db_config = db::Config { url: db_url.clone() };
let pool = Arc::new(db::Pool::new(&log.clone(), &db_config));

// Being a dev tool, we want to try this operation even if the schema
// doesn't match what we expect. So we use `DataStore::new_unchecked()`
// here. We will then check the schema version explicitly and warn the
// user if it doesn't match.
let datastore = Arc::new(
DataStore::new_unchecked(log.clone(), pool)
.map_err(|e| anyhow!(e).context("creating datastore"))?,
);
check_schema_version(&datastore).await;

let datastore = self.db_url_opts.connect(omdb, log).await?;
let opctx = OpContext::for_tests(log.clone(), datastore.clone());
match &self.command {
DbCommands::Rack(RackArgs { command: RackCommands::List }) => {
Expand Down
29 changes: 21 additions & 8 deletions dev-tools/omdb/src/bin/omdb/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,29 @@ struct Omdb {
command: OmdbCommands,
}

impl Omdb {
fn check_allow_destructive(&self) -> anyhow::Result<()> {
anyhow::ensure!(
self.allow_destructive,
"This command is potentially destructive. \
Pass the `-w` / `--destructive` flag to allow it."
);
Ok(())
mod check_allow_destructive {
/// Zero-size type that potentially-destructive functions can accept to
/// ensure `Omdb::check_allow_destructive` has been called.
// This is tucked away inside a module to prevent it from being constructed
// by anything other than `Omdb::check_allow_destructive`.
#[must_use]
pub(crate) struct DestructiveOperationToken(());

impl super::Omdb {
pub(crate) fn check_allow_destructive(
&self,
) -> anyhow::Result<DestructiveOperationToken> {
anyhow::ensure!(
self.allow_destructive,
"This command is potentially destructive. \
Pass the `-w` / `--destructive` flag to allow it."
);
Ok(DestructiveOperationToken(()))
}
}
}

impl Omdb {
async fn dns_lookup_all(
&self,
log: slog::Logger,
Expand Down
Loading

0 comments on commit 4f101be

Please sign in to comment.