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

RSS <-> Nexus handoff: Set an initial (disabled) target blueprint #5244

Merged
merged 13 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 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 common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ serde_human_bytes.workspace = true
serde_json.workspace = true
serde_with.workspace = true
slog.workspace = true
slog-error-chain.workspace = true
strum.workspace = true
test-strategy = { workspace = true, optional = true }
thiserror.workspace = true
Expand Down
11 changes: 10 additions & 1 deletion common/src/api/external/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use dropshot::HttpError;
use omicron_uuid_kinds::GenericUuid;
use serde::Deserialize;
use serde::Serialize;
use slog_error_chain::SlogInlineError;
use std::fmt::Display;
use uuid::Uuid;

Expand All @@ -26,7 +27,15 @@ use uuid::Uuid;
/// General best practices for error design apply here. Where possible, we want
/// to reuse existing variants rather than inventing new ones to distinguish
/// cases that no programmatic consumer needs to distinguish.
#[derive(Clone, Debug, Deserialize, thiserror::Error, PartialEq, Serialize)]
#[derive(
Clone,
Debug,
Deserialize,
thiserror::Error,
PartialEq,
Serialize,
SlogInlineError,
)]
pub enum Error {
/// An object needed as part of this operation was not found.
#[error("Object (of type {lookup_type:?}) not found: {type_name}")]
Expand Down
30 changes: 26 additions & 4 deletions common/src/api/external/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -650,16 +650,28 @@ impl From<ByteCount> for i64 {
pub struct Generation(u64);

impl Generation {
pub fn new() -> Generation {
pub const fn new() -> Generation {
Generation(1)
}

pub fn next(&self) -> Generation {
pub const fn from_u32(value: u32) -> Generation {
// `as` is a little distasteful because it allows lossy conversion, but
// (a) we know converting `u32` to `u64` will always succeed
// losslessly, and (b) it allows to make this function `const`, unlike
// if we were to use `u64::from(value)`.
Generation(value as u64)
}

pub const fn next(&self) -> Generation {
// It should technically be an operational error if this wraps or even
// exceeds the value allowed by an i64. But it seems unlikely enough to
// happen in practice that we can probably feel safe with this.
let next_gen = self.0 + 1;
assert!(next_gen <= u64::try_from(i64::MAX).unwrap());
// `as` is a little distasteful because it allows lossy conversion, but
// (a) we know converting `i64::MAX` to `u64` will always succeed
// losslessly, and (b) it allows to make this function `const`, unlike
// if we were to use `u64::try_from(i64::MAX).unwrap()`.
assert!(next_gen <= i64::MAX as u64);
Generation(next_gen)
}
}
Expand Down Expand Up @@ -697,11 +709,21 @@ impl TryFrom<i64> for Generation {
fn try_from(value: i64) -> Result<Self, Self::Error> {
Ok(Generation(
u64::try_from(value)
.map_err(|_| anyhow!("generation number too large"))?,
.map_err(|_| anyhow!("negative generation number"))?,
))
}
}

impl TryFrom<u64> for Generation {
type Error = anyhow::Error;

fn try_from(value: u64) -> Result<Self, Self::Error> {
i64::try_from(value)
.map_err(|_| anyhow!("generation number too large"))?;
Ok(Generation(value))
}
}

/// An RFC-1035-compliant hostname.
#[derive(
Clone, Debug, Deserialize, Display, Eq, PartialEq, SerializeDisplay,
Expand Down
1 change: 1 addition & 0 deletions nexus/db-queries/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ serde_urlencoded.workspace = true
serde_with.workspace = true
sled-agent-client.workspace = true
slog.workspace = true
slog-error-chain.workspace = true
static_assertions.workspace = true
steno.workspace = true
strum.workspace = true
Expand Down
30 changes: 25 additions & 5 deletions nexus/db-queries/src/db/datastore/deployment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,17 @@ impl DataStore {
&self,
opctx: &OpContext,
blueprint: &Blueprint,
) -> Result<(), Error> {
let conn = self.pool_connection_authorized(opctx).await?;
Self::blueprint_insert_on_connection(&conn, opctx, blueprint).await
}

/// Variant of [Self::blueprint_insert] which may be called from a
/// transaction context.
pub(crate) async fn blueprint_insert_on_connection(
conn: &async_bb8_diesel::Connection<DbConnection>,
opctx: &OpContext,
blueprint: &Blueprint,
) -> Result<(), Error> {
opctx
.authorize(authz::Action::Modify, &authz::BLUEPRINT_CONFIG)
Expand Down Expand Up @@ -154,8 +165,7 @@ impl DataStore {
// batch rather than making a bunch of round-trips to the database.
// We'd do that if we had an interface for doing that with bound
// parameters, etc. See oxidecomputer/omicron#973.
let pool = self.pool_connection_authorized(opctx).await?;
pool.transaction_async(|conn| async move {
conn.transaction_async(|conn| async move {
// Insert the row for the blueprint.
{
use db::schema::blueprint::dsl;
Expand Down Expand Up @@ -627,6 +637,18 @@ impl DataStore {
&self,
opctx: &OpContext,
target: BlueprintTarget,
) -> Result<(), Error> {
let conn = self.pool_connection_authorized(opctx).await?;
Self::blueprint_target_set_current_on_connection(&conn, opctx, target)
.await
}

/// Variant of [Self::blueprint_target_set_current] which may be called from
/// a transaction context.
pub(crate) async fn blueprint_target_set_current_on_connection(
conn: &async_bb8_diesel::Connection<DbConnection>,
opctx: &OpContext,
target: BlueprintTarget,
) -> Result<(), Error> {
opctx
.authorize(authz::Action::Modify, &authz::BLUEPRINT_CONFIG)
Expand All @@ -638,10 +660,8 @@ impl DataStore {
time_made_target: target.time_made_target,
};

let conn = self.pool_connection_authorized(opctx).await?;

query
.execute_async(&*conn)
.execute_async(conn)
.await
.map_err(|e| Error::from(query.decode_error(e)))?;

Expand Down
Loading
Loading