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

Implement silo level images #2772

Merged
merged 34 commits into from
Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
f3c30a8
Add image views
zephraph Apr 5, 2023
fa120f8
WIP impl project/silo images
zephraph Apr 5, 2023
3d470c8
Merge branch 'main' into image-silo-views
zephraph Apr 7, 2023
f6a99d5
Merge branch 'main' into image-silo-views
zephraph Apr 10, 2023
3343ead
Put a pin in current progress
zephraph Apr 10, 2023
094ec23
Add silo image id, name lookups
zephraph Apr 11, 2023
974f31e
Fixup some lookup issues
zephraph Apr 11, 2023
8c05776
Fix more import/life cycle/lookup references
zephraph Apr 11, 2023
1f77553
Merge branch 'main' into image-silo-views
zephraph Apr 11, 2023
532e6e5
Merge branch 'main' into image-silo-views
zephraph Apr 12, 2023
7016701
Fixup selectors
zephraph Apr 12, 2023
053d14e
Update image result type for project_id to be optional
zephraph Apr 12, 2023
96ab341
Add image promote
zephraph Apr 12, 2023
acdb77e
Enable listing silo images alongside project images
zephraph Apr 12, 2023
420af77
Merge branch 'main' into image-silo-views
zephraph Apr 13, 2023
4c58909
Merge branch 'main' into image-silo-views
zephraph Apr 14, 2023
3b1c576
Fix incorrect error message matching in test
zephraph Apr 14, 2023
5c34397
Merge branch 'main' into image-silo-views
zephraph Apr 14, 2023
68e649a
Attempt to resolve the stub test failure
zephraph Apr 14, 2023
e74f595
Fix incorrect project image lookup
zephraph Apr 17, 2023
239c37a
Fixup authz test for silo images
zephraph Apr 17, 2023
7fd9329
Remove insertable from the resources based off of db views
zephraph Apr 17, 2023
abfb634
Fixup promotion to have the correct authz (and to actually convert th…
zephraph Apr 17, 2023
093f09b
Ensure project image list w/ silos is scoped to the silo
zephraph Apr 17, 2023
c2a605e
Update image create test; default include_silo_images to false
zephraph Apr 17, 2023
fd48b21
Update nexus/types/src/external_api/params.rs
zephraph Apr 17, 2023
d48460b
Fix parsing issues w/ include_silo_images flag
zephraph Apr 17, 2023
da9d1e7
Change promote query to an update
zephraph Apr 18, 2023
ae67793
Add silo image creation test
zephraph Apr 18, 2023
5d9dd3f
Fix image promotion process
zephraph Apr 18, 2023
28c87cb
Remove unused import
zephraph Apr 18, 2023
c96d484
Add module docs for image models
zephraph Apr 18, 2023
c288e85
Fix clippy issues
zephraph Apr 18, 2023
2f9ef0c
Add promote endpoint to authz coverage
zephraph Apr 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions common/src/api/external/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,8 @@ pub enum ResourceType {
Dataset,
Disk,
Image,
SiloImage,
ProjectImage,
Instance,
IpPool,
InstanceNetworkInterface,
Expand Down
48 changes: 47 additions & 1 deletion common/src/sql/dbinit.sql
Original file line number Diff line number Diff line change
Expand Up @@ -905,7 +905,9 @@ CREATE TABLE omicron.public.image (
/* Indicates that the object has been deleted */
time_deleted TIMESTAMPTZ,

project_id UUID NOT NULL,
silo_id UUID NOT NULL,
project_id UUID,

volume_id UUID NOT NULL,

url STRING(8192),
Expand All @@ -916,7 +918,51 @@ CREATE TABLE omicron.public.image (
size_bytes INT NOT NULL
);

CREATE VIEW omicron.public.project_image AS
SELECT
id,
name,
description,
time_created,
time_modified,
time_deleted,
silo_id,
project_id,
Comment on lines +929 to +930
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that project images also include silo_id. This is necessary to convert them back to the base image type.

volume_id,
url,
os,
version,
digest,
block_size,
size_bytes
FROM
omicron.public.image
WHERE
project_id IS NOT NULL;

CREATE VIEW omicron.public.silo_image AS
SELECT
id,
name,
description,
time_created,
time_modified,
time_deleted,
silo_id,
volume_id,
url,
os,
version,
digest,
block_size,
size_bytes
FROM
omicron.public.image
WHERE
project_id IS NULL;

CREATE UNIQUE INDEX on omicron.public.image (
silo_id,
project_id,
name
) WHERE
Expand Down
28 changes: 28 additions & 0 deletions nexus/authz-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ enum PolarSnippet {
/// Generate it as a global resource, manipulable only to administrators
FleetChild,

/// Generate it as resource nested under the Silo
InSilo,

/// Generate it as a resource nested within a Project (either directly or
/// indirectly)
InProject,
Expand Down Expand Up @@ -200,6 +203,31 @@ fn do_authz_resource(
resource_name, resource_name,
),

// If this resource is directly inside a Silo, we only need to define
// permissions that are contingent on having roles on that Silo.
(PolarSnippet::InSilo, _) => format!(
r#"
resource {} {{
permissions = [
"list_children",
"modify",
"read",
"create_child",
];

relations = {{ containing_silo: Silo }};
"list_children" if "viewer" on "containing_silo";
"read" if "viewer" on "containing_silo";
"modify" if "collaborator" on "containing_silo";
"create_child" if "collaborator" on "containing_silo";
}}

has_relation(parent: Silo, "containing_silo", child: {})
if child.silo = parent;
"#,
resource_name, resource_name,
),
Comment on lines +206 to +229
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I patterned this off of InProject to avoid have to create a one-off authz rule for SiloImage. In the future if there's a normal resource that's a child of the silo we can use this polar snippet to avoid having to define custom rules for it. I haven't looked too closely at the other resources that live under the silo to see if any of them could be updated to use this snippet instead.


// If this resource is directly inside a Project, we only need to define
// permissions that are contingent on having roles on that Project.
(PolarSnippet::InProject, "Project") => format!(
Expand Down
163 changes: 161 additions & 2 deletions nexus/db-model/src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

//! Image, ProjectImage, and SiloImage are almost identical. Image is the model
//! for the underlying table and ProjectImage and SiloImage are backed by views
//! of that table. They have all the same fields except ProjectImage has both a
//! silo_id and a project_id, while SiloImage only has a silo_id. Image has a
//! silo_id and an optional project_id to cover both possibilities.

use super::{BlockSize, ByteCount, Digest};
use crate::schema::image;
use crate::schema::{image, project_image, silo_image};
use db_macros::Resource;
use nexus_types::external_api::views;
use nexus_types::identity::Resource;
use omicron_common::api::external::Error;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

// Project images
// Shared image definition
zephraph marked this conversation as resolved.
Show resolved Hide resolved
#[derive(
Queryable,
Insertable,
Expand All @@ -26,6 +33,29 @@ pub struct Image {
#[diesel(embed)]
pub identity: ImageIdentity,

pub silo_id: Uuid,
pub project_id: Option<Uuid>,

pub volume_id: Uuid,
pub url: Option<String>,
pub os: String,
pub version: String,
pub digest: Option<Digest>,
pub block_size: BlockSize,

#[diesel(column_name = size_bytes)]
pub size: ByteCount,
}

#[derive(
Queryable, Selectable, Clone, Debug, Resource, Serialize, Deserialize,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's notable that because project_image and silo_image are based on views they're not insertable resources (as noted by @luqmana in #2767)

)]
#[diesel(table_name = project_image)]
pub struct ProjectImage {
#[diesel(embed)]
pub identity: ProjectImageIdentity,

pub silo_id: Uuid,
pub project_id: Uuid,
pub volume_id: Uuid,
pub url: Option<String>,
Expand All @@ -38,6 +68,135 @@ pub struct Image {
pub size: ByteCount,
}

#[derive(
Queryable, Selectable, Clone, Debug, Resource, Serialize, Deserialize,
)]
#[diesel(table_name = silo_image)]
pub struct SiloImage {
#[diesel(embed)]
pub identity: SiloImageIdentity,

pub silo_id: Uuid,
pub volume_id: Uuid,
pub url: Option<String>,
pub os: String,
pub version: String,
pub digest: Option<Digest>,
pub block_size: BlockSize,

#[diesel(column_name = size_bytes)]
pub size: ByteCount,
}

impl TryFrom<Image> for ProjectImage {
type Error = Error;

fn try_from(image: Image) -> Result<Self, Self::Error> {
match image.project_id {
Some(project_id) => Ok(Self {
identity: ProjectImageIdentity {
id: image.id(),
name: image.name().clone().into(),
description: image.description().to_string(),
time_created: image.time_created(),
time_modified: image.time_modified(),
time_deleted: image.time_deleted(),
},
silo_id: image.silo_id,
project_id,
volume_id: image.volume_id,
url: image.url,
os: image.os,
version: image.version,
digest: image.digest,
block_size: image.block_size,
size: image.size,
}),
None => Err(Error::internal_error(
"tried to convert non-project image to project image",
)),
}
}
}

impl TryFrom<Image> for SiloImage {
type Error = Error;

fn try_from(image: Image) -> Result<Self, Self::Error> {
match image.project_id {
Some(_) => Err(Error::internal_error(
"tried to convert non-silo image to silo image",
)),
None => Ok(Self {
identity: SiloImageIdentity {
id: image.id(),
name: image.name().clone().into(),
description: image.description().to_string(),
time_created: image.time_created(),
time_modified: image.time_modified(),
time_deleted: image.time_deleted(),
},
silo_id: image.silo_id,
volume_id: image.volume_id,
url: image.url,
os: image.os,
version: image.version,
digest: image.digest,
block_size: image.block_size,
size: image.size,
}),
}
}
}

impl From<ProjectImage> for Image {
fn from(image: ProjectImage) -> Self {
Self {
identity: ImageIdentity {
id: image.id(),
name: image.name().clone().into(),
description: image.description().to_string(),
time_created: image.time_created(),
time_modified: image.time_modified(),
time_deleted: image.time_deleted(),
},
silo_id: image.silo_id,
project_id: Some(image.project_id),
volume_id: image.volume_id,
url: image.url,
os: image.os,
version: image.version,
digest: image.digest,
block_size: image.block_size,
size: image.size,
}
}
}

impl From<SiloImage> for Image {
fn from(image: SiloImage) -> Self {
Self {
identity: ImageIdentity {
id: image.id(),
name: image.name().clone().into(),
description: image.description().to_string(),
time_created: image.time_created(),
time_modified: image.time_modified(),
time_deleted: image.time_deleted(),
},
silo_id: image.silo_id,
project_id: None,
volume_id: image.volume_id,
url: image.url,
os: image.os,
version: image.version,
digest: image.digest,
block_size: image.block_size,
size: image.size,
}
}
}

impl From<Image> for views::Image {
fn from(image: Image) -> Self {
Self {
Expand Down
3 changes: 2 additions & 1 deletion nexus/db-model/src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use super::{Disk, Generation, Image, Instance, Name, Snapshot, Vpc};
use super::{Disk, Generation, Instance, Name, Snapshot, Vpc};
use crate::collection::DatastoreCollectionConfig;
use crate::schema::{disk, image, instance, project, snapshot, vpc};
use crate::Image;
use chrono::{DateTime, Utc};
use db_macros::Resource;
use nexus_types::external_api::params;
Expand Down
43 changes: 43 additions & 0 deletions nexus/db-model/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,27 @@ table! {
time_created -> Timestamptz,
time_modified -> Timestamptz,
time_deleted -> Nullable<Timestamptz>,
silo_id -> Uuid,
project_id -> Nullable<Uuid>,
volume_id -> Uuid,
url -> Nullable<Text>,
os -> Text,
version -> Text,
digest -> Nullable<Text>,
block_size -> crate::BlockSizeEnum,
size_bytes -> Int8,
}
}

table! {
project_image (id) {
id -> Uuid,
name -> Text,
description -> Text,
time_created -> Timestamptz,
time_modified -> Timestamptz,
time_deleted -> Nullable<Timestamptz>,
silo_id -> Uuid,
project_id -> Uuid,
volume_id -> Uuid,
url -> Nullable<Text>,
Expand All @@ -48,6 +69,25 @@ table! {
}
}

table! {
silo_image (id) {
id -> Uuid,
name -> Text,
description -> Text,
time_created -> Timestamptz,
time_modified -> Timestamptz,
time_deleted -> Nullable<Timestamptz>,
silo_id -> Uuid,
volume_id -> Uuid,
url -> Nullable<Text>,
os -> Text,
version -> Text,
digest -> Nullable<Text>,
block_size -> crate::BlockSizeEnum,
size_bytes -> Int8,
}
}

table! {
global_image (id) {
id -> Uuid,
Expand Down Expand Up @@ -848,6 +888,9 @@ joinable!(ip_pool_range -> ip_pool (ip_pool_id));
allow_tables_to_appear_in_same_query!(
dataset,
disk,
image,
project_image,
silo_image,
instance,
metric_producer,
network_interface,
Expand Down
Loading