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

Fix unique constraints for images #3254

Merged
merged 11 commits into from
Jul 6, 2023
12 changes: 11 additions & 1 deletion common/src/sql/dbinit.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1033,12 +1033,22 @@ FROM
WHERE
project_id IS NULL;

/* Index for silo images */
CREATE UNIQUE INDEX on omicron.public.image (
silo_id,
name
) WHERE
time_deleted is NULL AND
project_id is NULL;

/* Index for project images */
CREATE UNIQUE INDEX on omicron.public.image (
silo_id,
project_id,
name
) WHERE
time_deleted is NULL;
time_deleted is NULL AND
project_id is NOT NULL;

CREATE TYPE omicron.public.snapshot_state AS ENUM (
'creating',
Expand Down
38 changes: 37 additions & 1 deletion nexus/tests/integration_tests/images.rs
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ async fn test_make_disk_from_image_too_small(
}

#[nexus_test]
async fn test_image_access(cptestctx: &ControlPlaneTestContext) {
async fn test_image_promotion(cptestctx: &ControlPlaneTestContext) {
let client = &cptestctx.external_client;
DiskTest::new(&cptestctx).await;

Expand Down Expand Up @@ -588,11 +588,47 @@ async fn test_image_access(cptestctx: &ControlPlaneTestContext) {
assert_eq!(silo_images.len(), 1);
assert_eq!(silo_images[0].identity.name, "alpine-edge");

// Ensure there are no more project images
let project_images = NexusRequest::object_get(client, &images_url)
.authn_as(AuthnMode::PrivilegedUser)
.execute_and_parse_unwrap::<ResultsPage<views::Image>>()
.await
.items;

assert_eq!(project_images.len(), 0);

let silo_image_url = format!("/v1/images/{}", image_id);
let silo_image = NexusRequest::object_get(client, &silo_image_url)
.authn_as(AuthnMode::PrivilegedUser)
.execute_and_parse_unwrap::<views::Image>()
.await;

assert_eq!(silo_image.identity.id, image_id);

// Create another project image
zephraph marked this conversation as resolved.
Show resolved Hide resolved
NexusRequest::objects_post(client, &images_url, &image_create_params)
.authn_as(AuthnMode::PrivilegedUser)
.execute_and_parse_unwrap::<views::Image>()
.await;

// Ensure project image was created
let project_images = NexusRequest::object_get(client, &images_url)
.authn_as(AuthnMode::PrivilegedUser)
.execute_and_parse_unwrap::<ResultsPage<views::Image>>()
.await
.items;

assert_eq!(project_images.len(), 1);
assert_eq!(project_images[0].identity.name, "alpine-edge");

NexusRequest::new(
RequestBuilder::new(client, http::Method::POST, &promote_url)
.expect_status(Some(StatusCode::BAD_REQUEST)),
)
.authn_as(AuthnMode::PrivilegedUser)
.execute()
.await
.expect("unexpected success")
.parsed_body::<dropshot::HttpErrorResponseBody>()
.unwrap();
}