-
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
[affinity] Allow listing affinity/anti-affinity groups per-instance #7857
base: affinity-query-commentary
Are you sure you want to change the base?
Changes from all commits
d8fc977
8e1f3f9
8f460a2
a1faf19
7d9c4d1
d9799ee
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 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -1523,6 +1523,38 @@ impl super::Nexus { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Ok(disk) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/// Lists affinity groups to which this instance belongs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
pub(crate) async fn instance_list_affinity_groups( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
&self, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
opctx: &OpContext, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
instance_lookup: &lookup::Instance<'_>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
pagparams: &PaginatedBy<'_>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) -> ListResultVec<db::model::AffinityGroup> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let (.., authz_instance) = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
instance_lookup.lookup_for(authz::Action::ListChildren).await?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.db_datastore | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.instance_list_affinity_groups(opctx, &authz_instance, pagparams) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.await | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/// Lists anti-affinity groups to which this instance belongs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
pub(crate) async fn instance_list_anti_affinity_groups( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
&self, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
opctx: &OpContext, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
instance_lookup: &lookup::Instance<'_>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
pagparams: &PaginatedBy<'_>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) -> ListResultVec<db::model::AntiAffinityGroup> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let (.., authz_instance) = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
instance_lookup.lookup_for(authz::Action::ListChildren).await?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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. Should this (also or instead) check list children on projects since the groups are children of the project? I think that check is implied by this check because anyone with viewer+ on the project has list children on the project and the instance, but that's sort of incidental. It might be better if this check was more direct. omicron/nexus/auth/src/authz/omicron.polar Lines 178 to 195 in 0c92213
omicron/nexus/authz-macros/src/lib.rs Lines 372 to 395 in 0c92213
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 see we do the same for listing instance disks, which are directly analogous from an authz POV. Seems fine? omicron/nexus/src/app/instance.rs Lines 1410 to 1422 in 0c92213
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.db_datastore | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.instance_list_anti_affinity_groups( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
opctx, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
&authz_instance, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
pagparams, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.await | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/// Invoked by a sled agent to publish an updated runtime state for an | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/// Instance. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
pub(crate) async fn notify_vmm_updated( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -75,6 +75,11 @@ impl<T: AffinityGroupish> ProjectScopedApiHelper<'_, T> { | |
.await | ||
} | ||
|
||
async fn instance_groups_list(&self, instance: &str) -> Vec<T::Group> { | ||
let url = instance_groups_url(T::URL_COMPONENT, instance, self.project); | ||
objects_list_page_authz(&self.client, &url).await.items | ||
} | ||
|
||
async fn groups_list(&self) -> Vec<T::Group> { | ||
let url = groups_url(T::URL_COMPONENT, self.project); | ||
objects_list_page_authz(&self.client, &url).await.items | ||
|
@@ -401,6 +406,15 @@ impl AffinityGroupish for AntiAffinityType { | |
} | ||
} | ||
|
||
fn instance_groups_url( | ||
ty: &str, | ||
instance: &str, | ||
project: Option<&str>, | ||
) -> String { | ||
let query_params = project_query_param_suffix(project); | ||
format!("/v1/instances/{instance}/{ty}{query_params}") | ||
} | ||
|
||
fn groups_url(ty: &str, project: Option<&str>) -> String { | ||
let query_params = project_query_param_suffix(project); | ||
format!("/v1/{ty}{query_params}") | ||
|
@@ -755,6 +769,49 @@ async fn test_group_crud<T: AffinityGroupish>(client: &ClientTestContext) { | |
assert!(groups.is_empty()); | ||
} | ||
|
||
#[nexus_test] | ||
async fn test_affinity_instance_group_list( | ||
cptestctx: &ControlPlaneTestContext, | ||
) { | ||
let external_client = &cptestctx.external_client; | ||
test_instance_group_list::<AffinityType>(external_client).await; | ||
} | ||
|
||
#[nexus_test] | ||
async fn test_anti_affinity_instance_group_list( | ||
cptestctx: &ControlPlaneTestContext, | ||
) { | ||
let external_client = &cptestctx.external_client; | ||
test_instance_group_list::<AntiAffinityType>(external_client).await; | ||
} | ||
|
||
async fn test_instance_group_list<T: AffinityGroupish>( | ||
client: &ClientTestContext, | ||
) { | ||
const PROJECT_NAME: &'static str = "test-project"; | ||
|
||
let api = ApiHelper::new(client); | ||
|
||
// Create an IP pool and project that we'll use for testing. | ||
create_default_ip_pool(&client).await; | ||
api.create_project(PROJECT_NAME).await; | ||
|
||
let project_api = api.use_project::<T>(PROJECT_NAME); | ||
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. WHAT IS THIS BEAUTIFUL API 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. a hackery of query parameter appending for the affinity tests... but feel free to re-use it, if you want! |
||
|
||
project_api.create_stopped_instance("test-instance").await; | ||
let groups = project_api.instance_groups_list("test-instance").await; | ||
assert!(groups.is_empty(), "New instance should not belong to any groups"); | ||
|
||
project_api.group_create("yes-group").await; | ||
project_api.group_create("no-group").await; | ||
|
||
project_api.group_member_add("yes-group", "test-instance").await; | ||
|
||
let groups = project_api.instance_groups_list("test-instance").await; | ||
assert_eq!(groups.len(), 1); | ||
assert_eq!(groups[0].identity().name, "yes-group"); | ||
} | ||
|
||
#[nexus_test] | ||
async fn test_affinity_group_project_selector( | ||
cptestctx: &ControlPlaneTestContext, | ||
|
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.
I typed this up
but I answered my own question. Putting it here for posterity: the use of
PaginatedByNameOrId
in the query params on the endpoint means this takes asort_by=NameOrIdSortMode
param that lets you specify sort by ID, name asc, or name desc.