Skip to content

Commit

Permalink
Create quotas during silo creation
Browse files Browse the repository at this point in the history
  • Loading branch information
zephraph committed Dec 4, 2023
1 parent 5c50ce5 commit 3346a33
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 24 deletions.
32 changes: 30 additions & 2 deletions nexus/db-model/src/quota.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
use crate::schema::silo_quotas;
use chrono::{DateTime, Utc};
use nexus_types::external_api::views;
use nexus_types::external_api::{params, views};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

#[derive(
Queryable, Insertable, Debug, Clone, Selectable, Serialize, Deserialize,
Queryable,
Insertable,
Debug,
Clone,
Selectable,
Serialize,
Deserialize,
AsChangeset,
)]
#[diesel(table_name = silo_quotas)]
pub struct SiloQuotas {
Expand Down Expand Up @@ -54,3 +61,24 @@ impl From<views::SiloQuotas> for SiloQuotas {
}
}
}

// Describes a set of updates for the [`SiloQuotas`] model.
#[derive(AsChangeset)]
#[diesel(table_name = silo_quotas)]
pub struct SiloQuotasUpdate {
pub cpus: Option<i64>,
pub memory: Option<i64>,
pub storage: Option<i64>,
pub time_modified: DateTime<Utc>,
}

impl From<params::SiloQuotasUpdate> for SiloQuotasUpdate {
fn from(params: params::SiloQuotasUpdate) -> Self {
Self {
cpus: params.cpus,
memory: params.memory,
storage: params.storage,
time_modified: Utc::now(),
}
}
}
49 changes: 29 additions & 20 deletions nexus/db-queries/src/db/datastore/quota.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ use crate::db;
use crate::db::error::public_error_from_diesel;
use crate::db::error::ErrorHandler;
use crate::db::pagination::paginated;
use crate::db::pool::DbConnection;
use crate::db::TransactionError;
use async_bb8_diesel::AsyncConnection;
use async_bb8_diesel::AsyncRunQueryDsl;
use chrono::Utc;
use diesel::prelude::*;
use nexus_db_model::SiloQuotas;
use nexus_types::external_api::params;
use omicron_common::api::external::CreateResult;
use nexus_db_model::SiloQuotasUpdate;
use omicron_common::api::external::DataPageParams;
use omicron_common::api::external::Error;
use omicron_common::api::external::ListResultVec;
Expand All @@ -24,46 +25,54 @@ impl DataStore {
pub async fn silo_quotas_create(
&self,
opctx: &OpContext,
conn: &async_bb8_diesel::Connection<DbConnection>,
authz_silo: &authz::Silo,
quotas: SiloQuotas,
) -> CreateResult<SiloQuotas> {
) -> Result<(), Error> {
opctx.authorize(authz::Action::Modify, authz_silo).await?;
let silo_id = authz_silo.id();
use db::schema::silo_quotas::dsl;

diesel::insert_into(dsl::silo_quotas)
.values(quotas)
.returning(SiloQuotas::as_returning())
.get_result_async(&*self.pool_connection_authorized(opctx).await?)
.await
.map_err(|e| {
public_error_from_diesel(
let result = conn
.transaction_async(|c| async move {
diesel::insert_into(dsl::silo_quotas)
.values(quotas)
.execute_async(&c)
.await
.map_err(TransactionError::CustomError)
})
.await;

match result {
Ok(_) => Ok(()),
Err(TransactionError::CustomError(e)) => {
// TODO: Is this the right error handler?
Err(public_error_from_diesel(e, ErrorHandler::Server))
}
Err(TransactionError::Database(e)) => {
Err(public_error_from_diesel(
e,
ErrorHandler::Conflict(
ResourceType::SiloQuotas,
&silo_id.to_string(),
),
)
})
))
}
}
}

pub async fn silo_update_quota(
&self,
opctx: &OpContext,
authz_silo: &authz::Silo,
updates: params::SiloQuotasUpdate,
updates: SiloQuotasUpdate,
) -> UpdateResult<SiloQuotas> {
opctx.authorize(authz::Action::Modify, authz_silo).await?;
use db::schema::silo_quotas::dsl;
let silo_id = authz_silo.id();
diesel::update(dsl::silo_quotas)
.filter(dsl::silo_id.eq(silo_id))
.set((
dsl::time_modified.eq(Utc::now()),
dsl::cpus.eq(updates.cpus),
dsl::memory.eq(updates.memory),
dsl::storage.eq(updates.storage),
))
.set(updates)
.returning(SiloQuotas::as_returning())
.get_result_async(&*self.pool_connection_authorized(opctx).await?)
.await
Expand Down
14 changes: 14 additions & 0 deletions nexus/db-queries/src/db/datastore/silo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use chrono::Utc;
use diesel::prelude::*;
use nexus_db_model::Certificate;
use nexus_db_model::ServiceKind;
use nexus_db_model::SiloQuotas;
use nexus_types::external_api::params;
use nexus_types::external_api::shared;
use nexus_types::external_api::shared::SiloRole;
Expand Down Expand Up @@ -255,6 +256,19 @@ impl DataStore {

self.dns_update(nexus_opctx, &conn, dns_update).await?;

self.silo_quotas_create(
opctx,
&conn,
&authz_silo,
SiloQuotas::new(
authz_silo.id(),
new_silo_params.quotas.cpus,
new_silo_params.quotas.memory,
new_silo_params.quotas.storage,
),
)
.await?;

Ok(silo)
})
.await
Expand Down
12 changes: 12 additions & 0 deletions nexus/db-queries/src/db/fixed_data/silo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ lazy_static! {
name: "default-silo".parse().unwrap(),
description: "default silo".to_string(),
},
// TODO: Should the default silo have a quota? If so, what should the defaults be?
quotas: params::SiloQuotasCreate {
cpus: 0,
memory: 0,
storage: 0,
},
discoverable: false,
identity_mode: shared::SiloIdentityMode::LocalOnly,
admin_group_name: None,
Expand All @@ -49,6 +55,12 @@ lazy_static! {
name: "oxide-internal".parse().unwrap(),
description: "Built-in internal Silo.".to_string(),
},
// TODO: Should the internal silo have a quota? If so, what should the defaults be?
quotas: params::SiloQuotasCreate {
cpus: 0,
memory: 0,
storage: 0,
},
discoverable: false,
identity_mode: shared::SiloIdentityMode::LocalOnly,
admin_group_name: None,
Expand Down
2 changes: 1 addition & 1 deletion nexus/src/app/quota.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl super::Nexus {
let (.., authz_silo) =
silo_lookup.lookup_for(authz::Action::Modify).await?;
self.db_datastore
.silo_update_quota(opctx, &authz_silo, updates.clone())
.silo_update_quota(opctx, &authz_silo, updates.clone().into())
.await
}
}
2 changes: 2 additions & 0 deletions nexus/src/app/rack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ impl super::Nexus {
name: request.recovery_silo.silo_name,
description: "built-in recovery Silo".to_string(),
},
// TODO: Should the recovery silo have a quota? If so, what should the defaults be?
quotas: params::SiloQuotasCreate { cpus: 0, memory: 0, storage: 0 },
discoverable: false,
identity_mode: SiloIdentityMode::LocalOnly,
admin_group_name: None,
Expand Down
5 changes: 5 additions & 0 deletions nexus/test-utils/src/resource_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,11 @@ pub async fn create_silo(
name: silo_name.parse().unwrap(),
description: "a silo".to_string(),
},
quotas: params::SiloQuotasCreate {
cpus: 36,
memory: 1000,
storage: 100000,
},
discoverable,
identity_mode,
admin_group_name: None,
Expand Down
12 changes: 11 additions & 1 deletion nexus/types/src/external_api/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ pub struct SiloCreate {
/// endpoints. These should be valid for the Silo's DNS name(s).
pub tls_certificates: Vec<CertificateCreate>,

/// Initial quotas for the new Silo
pub quotas: SiloQuotasCreate,

/// Mapping of which Fleet roles are conferred by each Silo role
///
/// The default is that no Fleet roles are conferred by any Silo roles
Expand All @@ -272,12 +275,19 @@ pub struct SiloCreate {
}

#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub struct SiloQuotasUpdate {
pub struct SiloQuotasCreate {
pub cpus: i64,
pub memory: i64,
pub storage: i64,
}

#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub struct SiloQuotasUpdate {
pub cpus: Option<i64>,
pub memory: Option<i64>,
pub storage: Option<i64>,
}

/// Create-time parameters for a `User`
#[derive(Clone, Deserialize, Serialize, JsonSchema)]
pub struct UserCreate {
Expand Down

0 comments on commit 3346a33

Please sign in to comment.