-
Notifications
You must be signed in to change notification settings - Fork 42
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
Add resource limits #4605
Add resource limits #4605
Changes from 19 commits
b59092c
159644c
21ba99c
0127dd1
5c50ce5
3346a33
501f5ac
1894eb3
9e51431
caf371b
96877b2
8025899
a4002ff
76c722f
f8e2afc
3d4ebe2
d317629
dcf974e
83c9872
7e779a0
9859e12
45eb0ef
55ff0b2
2ee6d36
4c58f9e
3b014c8
bd950bc
4b0d236
0ad4340
4f6d49f
4111cc2
501563b
f15a451
569b995
ac8a2f3
43beca8
2c11210
025cd84
8a7794b
f8955a5
77f6d29
760675d
371bc98
de951a7
410d3ae
cd22388
e86ec72
5974325
c602e29
fe87ca8
11ab326
e369851
635d1ba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
use super::ByteCount; | ||
use crate::schema::silo_quotas; | ||
use chrono::{DateTime, Utc}; | ||
use nexus_types::external_api::{params, views}; | ||
use serde::{Deserialize, Serialize}; | ||
use uuid::Uuid; | ||
|
||
#[derive( | ||
Queryable, | ||
Insertable, | ||
Debug, | ||
Clone, | ||
Selectable, | ||
Serialize, | ||
Deserialize, | ||
AsChangeset, | ||
)] | ||
#[diesel(table_name = silo_quotas)] | ||
pub struct SiloQuotas { | ||
pub silo_id: Uuid, | ||
pub time_created: DateTime<Utc>, | ||
pub time_modified: DateTime<Utc>, | ||
|
||
/// The number of CPUs that this silo is allowed to use | ||
pub cpus: i64, | ||
|
||
/// The amount of memory (in bytes) that this silo is allowed to use | ||
#[diesel(column_name = memory_bytes)] | ||
pub memory: ByteCount, | ||
|
||
/// The amount of storage (in bytes) that this silo is allowed to use | ||
#[diesel(column_name = storage_bytes)] | ||
pub storage: ByteCount, | ||
} | ||
|
||
impl SiloQuotas { | ||
pub fn new( | ||
silo_id: Uuid, | ||
cpus: i64, | ||
memory: ByteCount, | ||
storage: ByteCount, | ||
) -> Self { | ||
Self { | ||
silo_id, | ||
time_created: Utc::now(), | ||
time_modified: Utc::now(), | ||
cpus, | ||
memory, | ||
storage, | ||
} | ||
} | ||
} | ||
|
||
impl From<SiloQuotas> for views::SiloQuotas { | ||
fn from(silo_quotas: SiloQuotas) -> Self { | ||
Self { | ||
silo_id: silo_quotas.silo_id, | ||
cpus: silo_quotas.cpus, | ||
memory: silo_quotas.memory.into(), | ||
storage: silo_quotas.storage.into(), | ||
} | ||
} | ||
} | ||
|
||
impl From<views::SiloQuotas> for SiloQuotas { | ||
fn from(silo_quotas: views::SiloQuotas) -> Self { | ||
Self { | ||
silo_id: silo_quotas.silo_id, | ||
time_created: Utc::now(), | ||
time_modified: Utc::now(), | ||
cpus: silo_quotas.cpus, | ||
memory: silo_quotas.memory.into(), | ||
storage: silo_quotas.storage.into(), | ||
} | ||
} | ||
} | ||
|
||
// Describes a set of updates for the [`SiloQuotas`] model. | ||
#[derive(AsChangeset)] | ||
#[diesel(table_name = silo_quotas)] | ||
pub struct SiloQuotasUpdate { | ||
pub cpus: Option<i64>, | ||
#[diesel(column_name = memory_bytes)] | ||
pub memory: Option<i64>, | ||
#[diesel(column_name = storage_bytes)] | ||
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.map(|f| f.into()), | ||
storage: params.storage.map(|f| f.into()), | ||
time_modified: Utc::now(), | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
use super::DataStore; | ||
use crate::authz; | ||
use crate::context::OpContext; | ||
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 diesel::prelude::*; | ||
use nexus_db_model::SiloQuotas; | ||
use nexus_db_model::SiloQuotasUpdate; | ||
use omicron_common::api::external::DataPageParams; | ||
use omicron_common::api::external::Error; | ||
use omicron_common::api::external::ListResultVec; | ||
use omicron_common::api::external::ResourceType; | ||
use omicron_common::api::external::UpdateResult; | ||
use uuid::Uuid; | ||
|
||
impl DataStore { | ||
/// Creates new quotas for a silo. This is grouped with silo creation | ||
/// and shouldn't be called directly by the user. | ||
pub async fn silo_quotas_create( | ||
&self, | ||
opctx: &OpContext, | ||
conn: &async_bb8_diesel::Connection<DbConnection>, | ||
authz_silo: &authz::Silo, | ||
quotas: SiloQuotas, | ||
) -> Result<(), Error> { | ||
opctx.authorize(authz::Action::Modify, authz_silo).await?; | ||
let silo_id = authz_silo.id(); | ||
use db::schema::silo_quotas::dsl; | ||
|
||
let result = conn | ||
.transaction_async(|c| async move { | ||
diesel::insert_into(dsl::silo_quotas) | ||
.values(quotas) | ||
.execute_async(&c) | ||
.await | ||
.map_err(TransactionError::CustomError) | ||
zephraph marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}) | ||
.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: 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(updates) | ||
.returning(SiloQuotas::as_returning()) | ||
.get_result_async(&*self.pool_connection_authorized(opctx).await?) | ||
.await | ||
.map_err(|e| { | ||
public_error_from_diesel( | ||
e, | ||
ErrorHandler::Conflict( | ||
ResourceType::SiloQuotas, | ||
&silo_id.to_string(), | ||
), | ||
) | ||
}) | ||
} | ||
|
||
pub async fn silo_quotas_view( | ||
&self, | ||
opctx: &OpContext, | ||
authz_silo: &authz::Silo, | ||
) -> Result<SiloQuotas, Error> { | ||
opctx.authorize(authz::Action::Read, authz_silo).await?; | ||
use db::schema::silo_quotas::dsl; | ||
dsl::silo_quotas | ||
.filter(dsl::silo_id.eq(authz_silo.id())) | ||
.first_async(&*self.pool_connection_authorized(opctx).await?) | ||
.await | ||
.map_err(|e| public_error_from_diesel(e, ErrorHandler::Server)) | ||
} | ||
|
||
pub async fn fleet_list_quotas( | ||
&self, | ||
opctx: &OpContext, | ||
pagparams: &DataPageParams<'_, Uuid>, | ||
) -> ListResultVec<SiloQuotas> { | ||
opctx.authorize(authz::Action::ListChildren, &authz::FLEET).await?; | ||
use db::schema::silo_quotas::dsl; | ||
paginated(dsl::silo_quotas, dsl::silo_id, pagparams) | ||
.select(SiloQuotas::as_select()) | ||
.load_async(&*self.pool_connection_authorized(opctx).await?) | ||
.await | ||
.map_err(|e| public_error_from_diesel(e, ErrorHandler::Server)) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,6 +28,7 @@ use chrono::Utc; | |
use diesel::prelude::*; | ||
use nexus_db_model::Certificate; | ||
use nexus_db_model::ServiceKind; | ||
use nexus_db_model::SiloQuotas; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (as discussed on our call) It's down in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TODO for myself: create an issue for the bifurcation of silo creation |
||
use nexus_types::external_api::params; | ||
use nexus_types::external_api::shared; | ||
use nexus_types::external_api::shared::SiloRole; | ||
|
@@ -263,6 +264,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.into(), | ||
new_silo_params.quotas.storage.into(), | ||
), | ||
) | ||
.await?; | ||
|
||
Ok::<Silo, TransactionError<Error>>(silo) | ||
}) | ||
.await?; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would you need to go from view to model? Is this used?