-
Notifications
You must be signed in to change notification settings - Fork 40
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
IP pools data model and API rework #4261
Changes from 67 commits
580594e
3065a61
f3809b5
6404e27
94d0ebf
1de49fc
7259531
4c8c5ea
5386043
a1eb6e4
9b4edaa
49c9927
9c8d2d5
ff3a4e2
db29bd3
9cbc423
ee5549c
0ab0cfc
2b4f326
e2b7600
acda55a
80a4d22
9c101ac
32b2f75
9519ed1
d7a0bdb
e79b862
c60ea2a
fa9317a
2345f35
3226866
1dd01c1
e8d1205
a47a9a3
6ea14ad
63e340c
b871acf
2094a8b
9da8783
a678c83
31b476f
6a55e89
1b71818
75c0290
7ff15b9
85922cd
7194ee2
f9ed857
d1e9b0a
39a4a48
00f86a0
d74ccd4
ee12c4e
70724cb
69a482a
7c0ba9d
5db1f09
bbcd731
c761559
dcd76b3
6b2ffbc
e4e969b
80cb5d0
bf29052
32fcbc4
dc9de62
ee0f363
3830c2a
06fcc99
e02a0e6
ba1eb0e
ea4cbfa
8aa01b9
584cb14
4976404
16cbd29
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 |
---|---|---|
|
@@ -5,8 +5,10 @@ | |
//! Model types for IP Pools and the CIDR blocks therein. | ||
|
||
use crate::collection::DatastoreCollectionConfig; | ||
use crate::impl_enum_type; | ||
use crate::schema::ip_pool; | ||
use crate::schema::ip_pool_range; | ||
use crate::schema::ip_pool_resource; | ||
use crate::Name; | ||
use chrono::DateTime; | ||
use chrono::Utc; | ||
|
@@ -35,42 +37,23 @@ pub struct IpPool { | |
/// Child resource generation number, for optimistic concurrency control of | ||
/// the contained ranges. | ||
pub rcgen: i64, | ||
|
||
/// Silo, if IP pool is associated with a particular silo. One special use | ||
/// for this is associating a pool with the internal silo oxide-internal, | ||
/// which is used for internal services. If there is no silo ID, the | ||
/// pool is considered a fleet-wide pool and will be used for allocating | ||
/// instance IPs in silos that don't have their own pool. | ||
pub silo_id: Option<Uuid>, | ||
|
||
pub is_default: bool, | ||
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. Important! Taking this off the |
||
} | ||
|
||
impl IpPool { | ||
pub fn new( | ||
pool_identity: &external::IdentityMetadataCreateParams, | ||
silo_id: Option<Uuid>, | ||
is_default: bool, | ||
) -> Self { | ||
pub fn new(pool_identity: &external::IdentityMetadataCreateParams) -> Self { | ||
Self { | ||
identity: IpPoolIdentity::new( | ||
Uuid::new_v4(), | ||
pool_identity.clone(), | ||
), | ||
rcgen: 0, | ||
silo_id, | ||
is_default, | ||
} | ||
} | ||
} | ||
|
||
impl From<IpPool> for views::IpPool { | ||
fn from(pool: IpPool) -> Self { | ||
Self { | ||
identity: pool.identity(), | ||
silo_id: pool.silo_id, | ||
is_default: pool.is_default, | ||
} | ||
Self { identity: pool.identity() } | ||
} | ||
} | ||
|
||
|
@@ -93,6 +76,46 @@ impl From<params::IpPoolUpdate> for IpPoolUpdate { | |
} | ||
} | ||
|
||
impl_enum_type!( | ||
#[derive(SqlType, Debug, Clone, Copy, QueryId)] | ||
#[diesel(postgres_type(name = "ip_pool_resource_type"))] | ||
pub struct IpPoolResourceTypeEnum; | ||
|
||
#[derive(Clone, Copy, Debug, AsExpression, FromSqlRow, PartialEq)] | ||
#[diesel(sql_type = IpPoolResourceTypeEnum)] | ||
pub enum IpPoolResourceType; | ||
|
||
Silo => b"silo" | ||
); | ||
|
||
#[derive(Queryable, Insertable, Selectable, Clone, Debug)] | ||
#[diesel(table_name = ip_pool_resource)] | ||
pub struct IpPoolResource { | ||
pub ip_pool_id: Uuid, | ||
pub resource_type: IpPoolResourceType, | ||
pub resource_id: Uuid, | ||
pub is_default: bool, | ||
} | ||
|
||
/// Information required to delete an IP pool association. Comes from request | ||
/// params -- silo is a NameOrId and must be resolved to ID. | ||
#[derive(Clone, Debug)] | ||
pub struct IpPoolResourceDelete { | ||
pub ip_pool_id: Uuid, | ||
pub resource_type: IpPoolResourceType, | ||
pub resource_id: Uuid, | ||
} | ||
|
||
impl From<IpPoolResource> for views::IpPoolSilo { | ||
fn from(assoc: IpPoolResource) -> Self { | ||
Self { | ||
ip_pool_id: assoc.ip_pool_id, | ||
silo_id: assoc.resource_id, | ||
is_default: assoc.is_default, | ||
} | ||
} | ||
} | ||
|
||
/// A range of IP addresses for an IP Pool. | ||
#[derive(Queryable, Insertable, Selectable, Clone, Debug)] | ||
#[diesel(table_name = ip_pool_range)] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,22 +76,18 @@ impl DataStore { | |
.fetch_for(authz::Action::CreateChild) | ||
.await?; | ||
|
||
// If the named pool conflicts with user's current scope, i.e., | ||
// if it has a silo and it's different from the current silo, | ||
// then as far as IP allocation is concerned, that pool doesn't | ||
// exist. If the pool has no silo, it's fleet-scoped and can | ||
// always be used. | ||
let authz_silo_id = opctx.authn.silo_required()?.id(); | ||
if let Some(pool_silo_id) = pool.silo_id { | ||
if pool_silo_id != authz_silo_id { | ||
return Err(authz_pool.not_found()); | ||
} | ||
// If this pool is not linked to the current silo, 404 | ||
if self.ip_pool_fetch_link(opctx, pool.id()).await.is_err() { | ||
return Err(authz_pool.not_found()); | ||
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. This check now requires looking up the pool in the silo association table instead of just looking at the silo ID on the pool. 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. I was wondering if you'd need to pass through the |
||
} | ||
|
||
pool | ||
} | ||
// If no name given, use the default logic | ||
None => self.ip_pools_fetch_default(&opctx).await?, | ||
None => { | ||
let (.., pool) = self.ip_pools_fetch_default(&opctx).await?; | ||
pool | ||
} | ||
}; | ||
|
||
let pool_id = pool.identity.id; | ||
|
@@ -147,24 +143,29 @@ impl DataStore { | |
) -> CreateResult<ExternalIp> { | ||
let ip_id = Uuid::new_v4(); | ||
|
||
let pool_id = match params.pool { | ||
// TODO: NameOrId resolution should happen a level higher, in the nexus function | ||
let (.., authz_pool, pool) = match params.pool { | ||
Comment on lines
+146
to
+147
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. Maybe we could just create a separate issue about this. I think there's a few places in the networking APIs around BGP that have the same thing. 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. I don't think you were planning on it, but let's save the fix for this for another PR. |
||
Some(NameOrId::Name(name)) => { | ||
LookupPath::new(opctx, self) | ||
.ip_pool_name(&Name(name)) | ||
.fetch_for(authz::Action::Read) | ||
.await? | ||
.1 | ||
} | ||
Some(NameOrId::Id(id)) => { | ||
LookupPath::new(opctx, self) | ||
.ip_pool_id(id) | ||
.fetch_for(authz::Action::Read) | ||
.await? | ||
.1 | ||
} | ||
None => self.ip_pools_fetch_default(opctx).await?, | ||
}; | ||
|
||
let pool_id = pool.id(); | ||
|
||
// If this pool is not linked to the current silo, 404 | ||
if self.ip_pool_fetch_link(opctx, pool_id).await.is_err() { | ||
return Err(authz_pool.not_found()); | ||
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. Adding the same check (that the specified pool is linked to the current silo) for floating IPs. |
||
} | ||
.id(); | ||
|
||
let data = if let Some(ip) = params.address { | ||
IncompleteExternalIp::for_floating_explicit( | ||
|
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.
Necessary because we no longer create and link a default silo during rack setup. This comes up in a lot of places, most notably (in terms of line count) the integration tests. Where before we used a
populate_ip_pool
helper that added an IP range to the existing default pool, we now use acreate_default_ip_pool
helper, which creates the pool, adds a range, and links it to the default silo.