diff --git a/progenitor-impl/tests/output/nexus.out b/progenitor-impl/tests/output/nexus.out index 3f63411..c48b12b 100644 --- a/progenitor-impl/tests/output/nexus.out +++ b/progenitor-impl/tests/output/nexus.out @@ -1,6 +1,26 @@ pub use progenitor_client::{ByteStream, Error, ResponseValue}; pub mod types { use serde::{Deserialize, Serialize}; + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct BlockSize(i64); + impl std::ops::Deref for BlockSize { + type Target = i64; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl std::convert::TryFrom for BlockSize { + type Error = &'static str; + fn try_from(value: i64) -> Result { + if ![512_i64, 2048_i64, 4096_i64].contains(&value) { + Err("invalid value") + } else { + Ok(Self(value)) + } + } + } + #[doc = "A count of bytes, typically used either for memory or storage capacity\n\nThe maximum supported byte count is [`i64::MAX`]. This makes it somewhat inconvenient to define constructors: a u32 constructor can be infallible, but an i64 constructor can fail (if the value is negative) and a u64 constructor can fail (if the value is larger than i64::MAX). We provide all of these for consumers' convenience."] #[derive(Serialize, Deserialize, Debug, Clone)] pub struct ByteCount(pub u64); @@ -14,29 +34,59 @@ pub mod types { #[doc = "The type of an individual datum of a metric."] #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] pub enum DatumType { + #[serde(rename = "bool")] Bool, + #[serde(rename = "i64")] I64, + #[serde(rename = "f64")] F64, + #[serde(rename = "string")] String, + #[serde(rename = "bytes")] Bytes, + #[serde(rename = "cumulative_i64")] CumulativeI64, + #[serde(rename = "cumulative_f64")] CumulativeF64, + #[serde(rename = "histogram_i64")] HistogramI64, + #[serde(rename = "histogram_f64")] HistogramF64, } impl ToString for DatumType { fn to_string(&self) -> String { match *self { - DatumType::Bool => "Bool".to_string(), - DatumType::I64 => "I64".to_string(), - DatumType::F64 => "F64".to_string(), - DatumType::String => "String".to_string(), - DatumType::Bytes => "Bytes".to_string(), - DatumType::CumulativeI64 => "CumulativeI64".to_string(), - DatumType::CumulativeF64 => "CumulativeF64".to_string(), - DatumType::HistogramI64 => "HistogramI64".to_string(), - DatumType::HistogramF64 => "HistogramF64".to_string(), + DatumType::Bool => "bool".to_string(), + DatumType::I64 => "i64".to_string(), + DatumType::F64 => "f64".to_string(), + DatumType::String => "string".to_string(), + DatumType::Bytes => "bytes".to_string(), + DatumType::CumulativeI64 => "cumulative_i64".to_string(), + DatumType::CumulativeF64 => "cumulative_f64".to_string(), + DatumType::HistogramI64 => "histogram_i64".to_string(), + DatumType::HistogramF64 => "histogram_f64".to_string(), + } + } + } + + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct Digest { + #[serde(rename = "type")] + pub type_: DigestType, + pub value: String, + } + + #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] + pub enum DigestType { + #[serde(rename = "sha256")] + Sha256, + } + + impl ToString for DigestType { + fn to_string(&self) -> String { + match *self { + DigestType::Sha256 => "sha256".to_string(), } } } @@ -44,11 +94,14 @@ pub mod types { #[doc = "Client view of an [`Disk`]"] #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Disk { + pub block_size: ByteCount, #[doc = "human-readable free-form text about a resource"] pub description: String, pub device_path: String, #[doc = "unique, immutable, system-controlled identifier for each resource"] pub id: uuid::Uuid, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub image_id: Option, #[doc = "unique, mutable, user-controlled identifier for each resource"] pub name: Name, pub project_id: uuid::Uuid, @@ -66,18 +119,17 @@ pub mod types { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct DiskCreate { pub description: String, + #[doc = "initial source for this disk"] + pub disk_source: DiskSource, pub name: Name, - #[doc = "size of the Disk"] + #[doc = "total size of the Disk in bytes"] pub size: ByteCount, - #[doc = "id for snapshot from which the Disk should be created, if any"] - #[serde(default, skip_serializing_if = "Option::is_none")] - pub snapshot_id: Option, } #[doc = "Parameters for the [`Disk`](omicron_common::api::external::Disk) to be attached or detached to an instance"] #[derive(Serialize, Deserialize, Debug, Clone)] pub struct DiskIdentifier { - pub disk: Name, + pub name: Name, } #[doc = "A single page of results"] @@ -90,6 +142,27 @@ pub mod types { pub next_page: Option, } + #[doc = "Different sources for a disk"] + #[derive(Serialize, Deserialize, Debug, Clone)] + #[serde(tag = "type")] + pub enum DiskSource { + #[doc = "Create a blank disk"] + #[serde(rename = "blank")] + Blank { + #[doc = "size of blocks for this Disk. valid values are: 512, 2048, or 4096"] + block_size: BlockSize, + }, + #[doc = "Create a disk from a disk snapshot"] + #[serde(rename = "snapshot")] + Snapshot { snapshot_id: uuid::Uuid }, + #[doc = "Create a disk from a project image"] + #[serde(rename = "image")] + Image { image_id: uuid::Uuid }, + #[doc = "Create a disk from a global image"] + #[serde(rename = "global_image")] + GlobalImage { image_id: uuid::Uuid }, + } + #[doc = "State of a Disk (primarily: attached or not)"] #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(tag = "state", content = "instance")] @@ -133,15 +206,17 @@ pub mod types { #[doc = "The source from which a field is derived, the target or metric."] #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] pub enum FieldSource { + #[serde(rename = "target")] Target, + #[serde(rename = "metric")] Metric, } impl ToString for FieldSource { fn to_string(&self) -> String { match *self { - FieldSource::Target => "Target".to_string(), - FieldSource::Metric => "Metric".to_string(), + FieldSource::Target => "target".to_string(), + FieldSource::Metric => "metric".to_string(), } } } @@ -149,40 +224,194 @@ pub mod types { #[doc = "The `FieldType` identifies the data type of a target or metric field."] #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] pub enum FieldType { + #[serde(rename = "string")] String, + #[serde(rename = "i64")] I64, + #[serde(rename = "ip_addr")] IpAddr, + #[serde(rename = "uuid")] Uuid, + #[serde(rename = "bool")] Bool, } impl ToString for FieldType { fn to_string(&self) -> String { match *self { - FieldType::String => "String".to_string(), - FieldType::I64 => "I64".to_string(), - FieldType::IpAddr => "IpAddr".to_string(), - FieldType::Uuid => "Uuid".to_string(), - FieldType::Bool => "Bool".to_string(), + FieldType::String => "string".to_string(), + FieldType::I64 => "i64".to_string(), + FieldType::IpAddr => "ip_addr".to_string(), + FieldType::Uuid => "uuid".to_string(), + FieldType::Bool => "bool".to_string(), } } } + #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] + pub enum FleetRoles { + #[serde(rename = "admin")] + Admin, + #[serde(rename = "collaborator")] + Collaborator, + #[serde(rename = "viewer")] + Viewer, + } + + impl ToString for FleetRoles { + fn to_string(&self) -> String { + match *self { + FleetRoles::Admin => "admin".to_string(), + FleetRoles::Collaborator => "collaborator".to_string(), + FleetRoles::Viewer => "viewer".to_string(), + } + } + } + + #[doc = "Client view of a [`Policy`], which describes how this resource may be accessed\n\nNote that the Policy only describes access granted explicitly for this resource. The policies of parent resources can also cause a user to have access to this resource."] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct FleetRolesPolicy { + #[doc = "Roles directly assigned on this resource"] + pub role_assignments: Vec, + } + + #[doc = "Describes the assignment of a particular role on a particular resource to a particular identity (user, group, etc.)\n\nThe resource is not part of this structure. Rather, [`RoleAssignment`]s are put into a [`Policy`] and that Policy is applied to a particular resource."] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct FleetRolesRoleAssignment { + pub identity_id: uuid::Uuid, + pub identity_type: IdentityType, + pub role_name: FleetRoles, + } + + #[doc = "Client view of global Images"] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct GlobalImage { + #[doc = "size of blocks in bytes"] + pub block_size: ByteCount, + #[doc = "human-readable free-form text about a resource"] + pub description: String, + #[doc = "Hash of the image contents, if applicable"] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub digest: Option, + #[doc = "unique, immutable, system-controlled identifier for each resource"] + pub id: uuid::Uuid, + #[doc = "unique, mutable, user-controlled identifier for each resource"] + pub name: Name, + #[doc = "total size in bytes"] + pub size: ByteCount, + #[doc = "timestamp when this resource was created"] + pub time_created: chrono::DateTime, + #[doc = "timestamp when this resource was last modified"] + pub time_modified: chrono::DateTime, + #[doc = "URL source of this image, if any"] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub url: Option, + #[doc = "Version of this, if any"] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub version: Option, + } + + #[doc = "A single page of results"] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct GlobalImageResultsPage { + #[doc = "list of items on this page of results"] + pub items: Vec, + #[doc = "token used to fetch the next page of results (if any)"] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub next_page: Option, + } + #[doc = "Supported set of sort modes for scanning by id only.\n\nCurrently, we only support scanning in ascending order."] #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] pub enum IdSortMode { - #[serde(rename = "id-ascending")] + #[serde(rename = "id_ascending")] IdAscending, } impl ToString for IdSortMode { fn to_string(&self) -> String { match *self { - IdSortMode::IdAscending => "id-ascending".to_string(), + IdSortMode::IdAscending => "id_ascending".to_string(), } } } + #[doc = "Describes what kind of identity is described by an id"] + #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] + pub enum IdentityType { + #[serde(rename = "silo_user")] + SiloUser, + } + + impl ToString for IdentityType { + fn to_string(&self) -> String { + match *self { + IdentityType::SiloUser => "silo_user".to_string(), + } + } + } + + #[doc = "Client view of project Images"] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct Image { + #[doc = "size of blocks in bytes"] + pub block_size: ByteCount, + #[doc = "human-readable free-form text about a resource"] + pub description: String, + #[doc = "Hash of the image contents, if applicable"] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub digest: Option, + #[doc = "unique, immutable, system-controlled identifier for each resource"] + pub id: uuid::Uuid, + #[doc = "unique, mutable, user-controlled identifier for each resource"] + pub name: Name, + #[doc = "The project the disk belongs to"] + pub project_id: uuid::Uuid, + #[doc = "total size in bytes"] + pub size: ByteCount, + #[doc = "timestamp when this resource was created"] + pub time_created: chrono::DateTime, + #[doc = "timestamp when this resource was last modified"] + pub time_modified: chrono::DateTime, + #[doc = "URL source of this image, if any"] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub url: Option, + #[doc = "Version of this, if any"] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub version: Option, + } + + #[doc = "Create-time parameters for an [`Image`](omicron_common::api::external::Image)"] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct ImageCreate { + #[doc = "block size in bytes"] + pub block_size: BlockSize, + pub description: String, + pub name: Name, + #[doc = "The source of the image's contents."] + pub source: ImageSource, + } + + #[doc = "A single page of results"] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct ImageResultsPage { + #[doc = "list of items on this page of results"] + pub items: Vec, + #[doc = "token used to fetch the next page of results (if any)"] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub next_page: Option, + } + + #[doc = "The source of the underlying image."] + #[derive(Serialize, Deserialize, Debug, Clone)] + #[serde(tag = "type", content = "src")] + pub enum ImageSource { + #[serde(rename = "url")] + Url(String), + #[serde(rename = "snapshot")] + Snapshot(uuid::Uuid), + } + #[doc = "Client view of an [`Instance`]"] #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Instance { @@ -222,13 +451,45 @@ pub mod types { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct InstanceCreate { pub description: String, + #[doc = "The disks to be created or attached for this instance."] + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub disks: Vec, pub hostname: String, pub memory: ByteCount, pub name: Name, pub ncpus: InstanceCpuCount, #[doc = "The network interfaces to be created for this instance."] - #[serde(default, skip_serializing_if = "Option::is_none")] - pub network_interfaces: Option, + #[serde(default = "instance_create_network_interfaces")] + pub network_interfaces: InstanceNetworkInterfaceAttachment, + #[doc = "User data for instance initialization systems (such as cloud-init). Must be a Base64-encoded string, as specified in RFC 4648 § 4 (+ and / characters with padding). Maximum 32 KiB unencoded data."] + #[serde(default)] + pub user_data: String, + } + + fn instance_create_network_interfaces() -> InstanceNetworkInterfaceAttachment { + InstanceNetworkInterfaceAttachment::Default + } + + #[doc = "Describe the instance's disks at creation time"] + #[derive(Serialize, Deserialize, Debug, Clone)] + #[serde(tag = "type")] + pub enum InstanceDiskAttachment { + #[doc = "During instance creation, create and attach disks"] + #[serde(rename = "create")] + Create { + description: String, + #[doc = "initial source for this disk"] + disk_source: DiskSource, + name: Name, + #[doc = "total size of the Disk in bytes"] + size: ByteCount, + }, + #[doc = "During instance creation, attach this disk"] + #[serde(rename = "attach")] + Attach { + #[doc = "A disk name to attach"] + name: Name, + }, } #[doc = "Migration parameters for an [`Instance`](omicron_common::api::external::Instance)"] @@ -242,16 +503,14 @@ pub mod types { #[serde(tag = "type", content = "params")] pub enum InstanceNetworkInterfaceAttachment { #[doc = "Create one or more `NetworkInterface`s for the `Instance`"] - Create(InstanceNetworkInterfaceCreate), + #[serde(rename = "create")] + Create(Vec), + #[serde(rename = "default")] Default, + #[serde(rename = "none")] None, } - #[derive(Serialize, Deserialize, Debug, Clone)] - pub struct InstanceNetworkInterfaceCreate { - pub params: Vec, - } - #[doc = "A single page of results"] #[derive(Serialize, Deserialize, Debug, Clone)] pub struct InstanceResultsPage { @@ -304,8 +563,8 @@ pub mod types { } } - #[doc = "An `IpNet` represents an IP network, either IPv4 or IPv6."] #[derive(Serialize, Deserialize, Debug, Clone)] + #[serde(untagged)] pub enum IpNet { V4(Ipv4Net), V6(Ipv6Net), @@ -369,20 +628,20 @@ pub mod types { #[doc = "Supported set of sort modes for scanning by name or id"] #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] pub enum NameOrIdSortMode { - #[serde(rename = "name-ascending")] + #[serde(rename = "name_ascending")] NameAscending, - #[serde(rename = "name-descending")] + #[serde(rename = "name_descending")] NameDescending, - #[serde(rename = "id-ascending")] + #[serde(rename = "id_ascending")] IdAscending, } impl ToString for NameOrIdSortMode { fn to_string(&self) -> String { match *self { - NameOrIdSortMode::NameAscending => "name-ascending".to_string(), - NameOrIdSortMode::NameDescending => "name-descending".to_string(), - NameOrIdSortMode::IdAscending => "id-ascending".to_string(), + NameOrIdSortMode::NameAscending => "name_ascending".to_string(), + NameOrIdSortMode::NameDescending => "name_descending".to_string(), + NameOrIdSortMode::IdAscending => "id_ascending".to_string(), } } } @@ -390,14 +649,14 @@ pub mod types { #[doc = "Supported set of sort modes for scanning by name only\n\nCurrently, we only support scanning in ascending order."] #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] pub enum NameSortMode { - #[serde(rename = "name-ascending")] + #[serde(rename = "name_ascending")] NameAscending, } impl ToString for NameSortMode { fn to_string(&self) -> String { match *self { - NameSortMode::NameAscending => "name-ascending".to_string(), + NameSortMode::NameAscending => "name_ascending".to_string(), } } } @@ -483,6 +742,38 @@ pub mod types { pub next_page: Option, } + #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] + pub enum OrganizationRoles { + #[serde(rename = "admin")] + Admin, + #[serde(rename = "collaborator")] + Collaborator, + } + + impl ToString for OrganizationRoles { + fn to_string(&self) -> String { + match *self { + OrganizationRoles::Admin => "admin".to_string(), + OrganizationRoles::Collaborator => "collaborator".to_string(), + } + } + } + + #[doc = "Client view of a [`Policy`], which describes how this resource may be accessed\n\nNote that the Policy only describes access granted explicitly for this resource. The policies of parent resources can also cause a user to have access to this resource."] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct OrganizationRolesPolicy { + #[doc = "Roles directly assigned on this resource"] + pub role_assignments: Vec, + } + + #[doc = "Describes the assignment of a particular role on a particular resource to a particular identity (user, group, etc.)\n\nThe resource is not part of this structure. Rather, [`RoleAssignment`]s are put into a [`Policy`] and that Policy is applied to a particular resource."] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct OrganizationRolesRoleAssignment { + pub identity_id: uuid::Uuid, + pub identity_type: IdentityType, + pub role_name: OrganizationRoles, + } + #[doc = "Updateable properties of an [`Organization`](crate::external_api::views::Organization)"] #[derive(Serialize, Deserialize, Debug, Clone)] pub struct OrganizationUpdate { @@ -525,6 +816,41 @@ pub mod types { pub next_page: Option, } + #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] + pub enum ProjectRoles { + #[serde(rename = "admin")] + Admin, + #[serde(rename = "collaborator")] + Collaborator, + #[serde(rename = "viewer")] + Viewer, + } + + impl ToString for ProjectRoles { + fn to_string(&self) -> String { + match *self { + ProjectRoles::Admin => "admin".to_string(), + ProjectRoles::Collaborator => "collaborator".to_string(), + ProjectRoles::Viewer => "viewer".to_string(), + } + } + } + + #[doc = "Client view of a [`Policy`], which describes how this resource may be accessed\n\nNote that the Policy only describes access granted explicitly for this resource. The policies of parent resources can also cause a user to have access to this resource."] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct ProjectRolesPolicy { + #[doc = "Roles directly assigned on this resource"] + pub role_assignments: Vec, + } + + #[doc = "Describes the assignment of a particular role on a particular resource to a particular identity (user, group, etc.)\n\nThe resource is not part of this structure. Rather, [`RoleAssignment`]s are put into a [`Policy`] and that Policy is applied to a particular resource."] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct ProjectRolesRoleAssignment { + pub identity_id: uuid::Uuid, + pub identity_type: IdentityType, + pub role_name: ProjectRoles, + } + #[doc = "Updateable properties of a [`Project`](crate::external_api::views::Project)"] #[derive(Serialize, Deserialize, Debug, Clone)] pub struct ProjectUpdate { @@ -637,13 +963,13 @@ pub mod types { pub kind: RouterRouteKind, #[doc = "unique, mutable, user-controlled identifier for each resource"] pub name: Name, - #[doc = "The VPC Router to which the route belongs."] - pub router_id: uuid::Uuid, pub target: RouteTarget, #[doc = "timestamp when this resource was created"] pub time_created: chrono::DateTime, #[doc = "timestamp when this resource was last modified"] pub time_modified: chrono::DateTime, + #[doc = "The VPC Router to which the route belongs."] + pub vpc_router_id: uuid::Uuid, } #[doc = "Create-time parameters for a [`RouterRoute`]"] @@ -751,6 +1077,76 @@ pub mod types { pub id: uuid::Uuid, } + #[doc = "Client view of a ['Silo']"] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct Silo { + #[doc = "human-readable free-form text about a resource"] + pub description: String, + #[doc = "A silo where discoverable is false can be retrieved only by its id - it will not be part of the \"list all silos\" output."] + pub discoverable: bool, + #[doc = "unique, immutable, system-controlled identifier for each resource"] + pub id: uuid::Uuid, + #[doc = "unique, mutable, user-controlled identifier for each resource"] + pub name: Name, + #[doc = "timestamp when this resource was created"] + pub time_created: chrono::DateTime, + #[doc = "timestamp when this resource was last modified"] + pub time_modified: chrono::DateTime, + } + + #[doc = "Create-time parameters for a [`Silo`](crate::external_api::views::Silo)"] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct SiloCreate { + pub description: String, + pub discoverable: bool, + pub name: Name, + } + + #[doc = "A single page of results"] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct SiloResultsPage { + #[doc = "list of items on this page of results"] + pub items: Vec, + #[doc = "token used to fetch the next page of results (if any)"] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub next_page: Option, + } + + #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] + pub enum SiloRoles { + #[serde(rename = "admin")] + Admin, + #[serde(rename = "collaborator")] + Collaborator, + #[serde(rename = "viewer")] + Viewer, + } + + impl ToString for SiloRoles { + fn to_string(&self) -> String { + match *self { + SiloRoles::Admin => "admin".to_string(), + SiloRoles::Collaborator => "collaborator".to_string(), + SiloRoles::Viewer => "viewer".to_string(), + } + } + } + + #[doc = "Client view of a [`Policy`], which describes how this resource may be accessed\n\nNote that the Policy only describes access granted explicitly for this resource. The policies of parent resources can also cause a user to have access to this resource."] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct SiloRolesPolicy { + #[doc = "Roles directly assigned on this resource"] + pub role_assignments: Vec, + } + + #[doc = "Describes the assignment of a particular role on a particular resource to a particular identity (user, group, etc.)\n\nThe resource is not part of this structure. Rather, [`RoleAssignment`]s are put into a [`Policy`] and that Policy is applied to a particular resource."] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct SiloRolesRoleAssignment { + pub identity_id: uuid::Uuid, + pub identity_type: IdentityType, + pub role_name: SiloRoles, + } + #[doc = "Client view of an [`Sled`]"] #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Sled { @@ -814,6 +1210,44 @@ pub mod types { pub next_page: Option, } + #[doc = "Client view of a [`SshKey`]"] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct SshKey { + #[doc = "human-readable free-form text about a resource"] + pub description: String, + #[doc = "unique, immutable, system-controlled identifier for each resource"] + pub id: uuid::Uuid, + #[doc = "unique, mutable, user-controlled identifier for each resource"] + pub name: Name, + #[doc = "SSH public key, e.g., `\"ssh-ed25519 AAAAC3NzaC...\"`"] + pub public_key: String, + #[doc = "The user to whom this key belongs"] + pub silo_user_id: uuid::Uuid, + #[doc = "timestamp when this resource was created"] + pub time_created: chrono::DateTime, + #[doc = "timestamp when this resource was last modified"] + pub time_modified: chrono::DateTime, + } + + #[doc = "Create-time parameters for an [`SshKey`](crate::external_api::views::SshKey)"] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct SshKeyCreate { + pub description: String, + pub name: Name, + #[doc = "SSH public key, e.g., `\"ssh-ed25519 AAAAC3NzaC...\"`"] + pub public_key: String, + } + + #[doc = "A single page of results"] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct SshKeyResultsPage { + #[doc = "list of items on this page of results"] + pub items: Vec, + #[doc = "token used to fetch the next page of results (if any)"] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub next_page: Option, + } + #[doc = "Names are constructed by concatenating the target and metric names with ':'. Target and metric names must be lowercase alphanumeric characters with '_' separating words."] #[derive(Serialize, Deserialize, Debug, Clone)] pub struct TimeseriesName(pub String); @@ -1212,10 +1646,6 @@ pub mod types { #[serde(default, skip_serializing_if = "Option::is_none")] pub description: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub ipv4_block: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub ipv6_block: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] pub name: Option, } @@ -1455,6 +1885,149 @@ impl Client { } } + #[doc = "List global images\n\nReturns a list of all the global images. Global images are returned sorted by creation date, with the most recent images appearing first.\n\nSends a `GET` request to `/images`\n\nArguments:\n- `limit`: Maximum number of items returned by a single call\n- `page_token`: Token returned by previous call to retreive the subsequent page\n- `sort_by`\n"] + pub async fn images_get<'a>( + &'a self, + limit: Option, + page_token: Option<&'a str>, + sort_by: Option, + ) -> Result, Error> { + let url = format!("{}/images", self.baseurl,); + let mut query = Vec::new(); + if let Some(v) = &limit { + query.push(("limit", v.to_string())); + } + + if let Some(v) = &page_token { + query.push(("page_token", v.to_string())); + } + + if let Some(v) = &sort_by { + query.push(("sort_by", v.to_string())); + } + + let request = self.client.get(url).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + #[doc = "List global images as a Stream\n\nReturns a list of all the global images. Global images are returned sorted by creation date, with the most recent images appearing first.\n\nSends repeated `GET` requests to `/images` until there are no more results.\n\nArguments:\n- `limit`: Maximum number of items returned by a single call\n- `sort_by`\n"] + pub fn images_get_stream<'a>( + &'a self, + limit: Option, + sort_by: Option, + ) -> impl futures::Stream>> + Unpin + '_ + { + use futures::StreamExt; + use futures::TryFutureExt; + use futures::TryStreamExt; + self.images_get(limit, None, sort_by) + .map_ok(move |page| { + let page = page.into_inner(); + let first = futures::stream::iter(page.items.into_iter().map(Ok)); + let rest = futures::stream::try_unfold(page.next_page, move |state| async move { + if state.is_none() { + Ok(None) + } else { + self.images_get(None, state.as_deref(), None) + .map_ok(|page| { + let page = page.into_inner(); + Some(( + futures::stream::iter(page.items.into_iter().map(Ok)), + page.next_page, + )) + }) + .await + } + }) + .try_flatten(); + first.chain(rest) + }) + .try_flatten_stream() + .boxed() + } + + #[doc = "Create a global image\n\nCreate a new global image. This image can then be used by any user as a base for instances.\n\nSends a `POST` request to `/images`"] + pub async fn images_post<'a>( + &'a self, + body: &'a types::ImageCreate, + ) -> Result, Error> { + let url = format!("{}/images", self.baseurl,); + let request = self.client.post(url).json(body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + #[doc = "Get a global image\n\nReturns the details of a specific global image.\n\nSends a `GET` request to `/images/{image_name}`"] + pub async fn images_get_image<'a>( + &'a self, + image_name: &'a types::Name, + ) -> Result, Error> { + let url = format!( + "{}/images/{}", + self.baseurl, + progenitor_client::encode_path(&image_name.to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + #[doc = "Delete a global image\n\nPermanently delete a global image. This operation cannot be undone. Any instances using the global image will continue to run, however new instances can not be created with this image.\n\nSends a `DELETE` request to `/images/{image_name}`"] + pub async fn images_delete_image<'a>( + &'a self, + image_name: &'a types::Name, + ) -> Result, Error> { + let url = format!( + "{}/images/{}", + self.baseurl, + progenitor_client::encode_path(&image_name.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 204u16 => Ok(ResponseValue::empty(response)), + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + #[doc = "Sends a `POST` request to `/login`"] pub async fn spoof_login<'a>( &'a self, @@ -1651,6 +2224,57 @@ impl Client { } } + #[doc = "Fetch the IAM policy for this Organization\n\nSends a `GET` request to `/organizations/{organization_name}/policy`\n\nArguments:\n- `organization_name`: The organization's unique name.\n"] + pub async fn organization_get_policy<'a>( + &'a self, + organization_name: &'a types::Name, + ) -> Result, Error> { + let url = format!( + "{}/organizations/{}/policy", + self.baseurl, + progenitor_client::encode_path(&organization_name.to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + #[doc = "Update the IAM policy for this Organization\n\nSends a `PUT` request to `/organizations/{organization_name}/policy`\n\nArguments:\n- `organization_name`: The organization's unique name.\n- `body`\n"] + pub async fn organization_put_policy<'a>( + &'a self, + organization_name: &'a types::Name, + body: &'a types::OrganizationRolesPolicy, + ) -> Result, Error> { + let url = format!( + "{}/organizations/{}/policy", + self.baseurl, + progenitor_client::encode_path(&organization_name.to_string()), + ); + let request = self.client.put(url).json(body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + #[doc = "List all projects\n\nSends a `GET` request to `/organizations/{organization_name}/projects`\n\nArguments:\n- `organization_name`: The organization's unique name.\n- `limit`: Maximum number of items returned by a single call\n- `page_token`: Token returned by previous call to retreive the subsequent page\n- `sort_by`\n"] pub async fn organization_projects_get<'a>( &'a self, @@ -2013,6 +2637,178 @@ impl Client { } } + #[doc = "List images\n\nList images in a project. The images are returned sorted by creation date, with the most recent images appearing first.\n\nSends a `GET` request to `/organizations/{organization_name}/projects/{project_name}/images`\n\nArguments:\n- `organization_name`: The organization's unique name.\n- `project_name`: The project's unique name within the organization.\n- `limit`: Maximum number of items returned by a single call\n- `page_token`: Token returned by previous call to retreive the subsequent page\n- `sort_by`\n"] + pub async fn project_images_get<'a>( + &'a self, + organization_name: &'a types::Name, + project_name: &'a types::Name, + limit: Option, + page_token: Option<&'a str>, + sort_by: Option, + ) -> Result, Error> { + let url = format!( + "{}/organizations/{}/projects/{}/images", + self.baseurl, + progenitor_client::encode_path(&organization_name.to_string()), + progenitor_client::encode_path(&project_name.to_string()), + ); + let mut query = Vec::new(); + if let Some(v) = &limit { + query.push(("limit", v.to_string())); + } + + if let Some(v) = &page_token { + query.push(("page_token", v.to_string())); + } + + if let Some(v) = &sort_by { + query.push(("sort_by", v.to_string())); + } + + let request = self.client.get(url).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + #[doc = "List images as a Stream\n\nList images in a project. The images are returned sorted by creation date, with the most recent images appearing first.\n\nSends repeated `GET` requests to `/organizations/{organization_name}/projects/{project_name}/images` until there are no more results.\n\nArguments:\n- `organization_name`: The organization's unique name.\n- `project_name`: The project's unique name within the organization.\n- `limit`: Maximum number of items returned by a single call\n- `sort_by`\n"] + pub fn project_images_get_stream<'a>( + &'a self, + organization_name: &'a types::Name, + project_name: &'a types::Name, + limit: Option, + sort_by: Option, + ) -> impl futures::Stream>> + Unpin + '_ { + use futures::StreamExt; + use futures::TryFutureExt; + use futures::TryStreamExt; + self.project_images_get(organization_name, project_name, limit, None, sort_by) + .map_ok(move |page| { + let page = page.into_inner(); + let first = futures::stream::iter(page.items.into_iter().map(Ok)); + let rest = futures::stream::try_unfold(page.next_page, move |state| async move { + if state.is_none() { + Ok(None) + } else { + self.project_images_get( + organization_name, + project_name, + None, + state.as_deref(), + None, + ) + .map_ok(|page| { + let page = page.into_inner(); + Some(( + futures::stream::iter(page.items.into_iter().map(Ok)), + page.next_page, + )) + }) + .await + } + }) + .try_flatten(); + first.chain(rest) + }) + .try_flatten_stream() + .boxed() + } + + #[doc = "Create an image\n\nCreate a new image in a project.\n\nSends a `POST` request to `/organizations/{organization_name}/projects/{project_name}/images`\n\nArguments:\n- `organization_name`: The organization's unique name.\n- `project_name`: The project's unique name within the organization.\n- `body`\n"] + pub async fn project_images_post<'a>( + &'a self, + organization_name: &'a types::Name, + project_name: &'a types::Name, + body: &'a types::ImageCreate, + ) -> Result, Error> { + let url = format!( + "{}/organizations/{}/projects/{}/images", + self.baseurl, + progenitor_client::encode_path(&organization_name.to_string()), + progenitor_client::encode_path(&project_name.to_string()), + ); + let request = self.client.post(url).json(body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + #[doc = "Get an image\n\nGet the details of a specific image in a project.\n\nSends a `GET` request to `/organizations/{organization_name}/projects/{project_name}/images/{image_name}`"] + pub async fn project_images_get_image<'a>( + &'a self, + organization_name: &'a types::Name, + project_name: &'a types::Name, + image_name: &'a types::Name, + ) -> Result, Error> { + let url = format!( + "{}/organizations/{}/projects/{}/images/{}", + self.baseurl, + progenitor_client::encode_path(&organization_name.to_string()), + progenitor_client::encode_path(&project_name.to_string()), + progenitor_client::encode_path(&image_name.to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + #[doc = "Delete an image\n\nPermanently delete an image from a project. This operation cannot be undone. Any instances in the project using the image will continue to run, however new instances can not be created with this image.\n\nSends a `DELETE` request to `/organizations/{organization_name}/projects/{project_name}/images/{image_name}`"] + pub async fn project_images_delete_image<'a>( + &'a self, + organization_name: &'a types::Name, + project_name: &'a types::Name, + image_name: &'a types::Name, + ) -> Result, Error> { + let url = format!( + "{}/organizations/{}/projects/{}/images/{}", + self.baseurl, + progenitor_client::encode_path(&organization_name.to_string()), + progenitor_client::encode_path(&project_name.to_string()), + progenitor_client::encode_path(&image_name.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 204u16 => Ok(ResponseValue::empty(response)), + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + #[doc = "List instances in a project\n\nSends a `GET` request to `/organizations/{organization_name}/projects/{project_name}/instances`\n\nArguments:\n- `organization_name`: The organization's unique name.\n- `project_name`: The project's unique name within the organization.\n- `limit`: Maximum number of items returned by a single call\n- `page_token`: Token returned by previous call to retreive the subsequent page\n- `sort_by`\n"] pub async fn project_instances_get<'a>( &'a self, @@ -2650,6 +3446,61 @@ impl Client { } } + #[doc = "Fetch the IAM policy for this Project\n\nSends a `GET` request to `/organizations/{organization_name}/projects/{project_name}/policy`\n\nArguments:\n- `organization_name`: The organization's unique name.\n- `project_name`: The project's unique name within the organization.\n"] + pub async fn organization_projects_get_project_policy<'a>( + &'a self, + organization_name: &'a types::Name, + project_name: &'a types::Name, + ) -> Result, Error> { + let url = format!( + "{}/organizations/{}/projects/{}/policy", + self.baseurl, + progenitor_client::encode_path(&organization_name.to_string()), + progenitor_client::encode_path(&project_name.to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + #[doc = "Update the IAM policy for this Project\n\nSends a `PUT` request to `/organizations/{organization_name}/projects/{project_name}/policy`\n\nArguments:\n- `organization_name`: The organization's unique name.\n- `project_name`: The project's unique name within the organization.\n- `body`\n"] + pub async fn organization_projects_put_project_policy<'a>( + &'a self, + organization_name: &'a types::Name, + project_name: &'a types::Name, + body: &'a types::ProjectRolesPolicy, + ) -> Result, Error> { + let url = format!( + "{}/organizations/{}/projects/{}/policy", + self.baseurl, + progenitor_client::encode_path(&organization_name.to_string()), + progenitor_client::encode_path(&project_name.to_string()), + ); + let request = self.client.put(url).json(body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + #[doc = "List snapshots in a project\n\nSends a `GET` request to `/organizations/{organization_name}/projects/{project_name}/snapshots`\n\nArguments:\n- `organization_name`: The organization's unique name.\n- `project_name`: The project's unique name within the organization.\n- `limit`: Maximum number of items returned by a single call\n- `page_token`: Token returned by previous call to retreive the subsequent page\n- `sort_by`\n"] pub async fn project_snapshots_get<'a>( &'a self, @@ -3866,6 +4717,47 @@ impl Client { .boxed() } + #[doc = "Fetch the top-level IAM policy\n\nSends a `GET` request to `/policy`"] + pub async fn policy_get<'a>( + &'a self, + ) -> Result, Error> { + let url = format!("{}/policy", self.baseurl,); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + #[doc = "Update the top-level IAM policy\n\nSends a `PUT` request to `/policy`"] + pub async fn policy_put<'a>( + &'a self, + body: &'a types::FleetRolesPolicy, + ) -> Result, Error> { + let url = format!("{}/policy", self.baseurl,); + let request = self.client.put(url).json(body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + #[doc = "List the built-in roles\n\nSends a `GET` request to `/roles`\n\nArguments:\n- `limit`: Maximum number of items returned by a single call\n- `page_token`: Token returned by previous call to retreive the subsequent page\n"] pub async fn roles_get<'a>( &'a self, @@ -4072,6 +4964,341 @@ impl Client { } } + #[doc = "List the current user's SSH public keys\n\nSends a `GET` request to `/session/me/sshkeys`\n\nArguments:\n- `limit`: Maximum number of items returned by a single call\n- `page_token`: Token returned by previous call to retreive the subsequent page\n- `sort_by`\n"] + pub async fn sshkeys_get<'a>( + &'a self, + limit: Option, + page_token: Option<&'a str>, + sort_by: Option, + ) -> Result, Error> { + let url = format!("{}/session/me/sshkeys", self.baseurl,); + let mut query = Vec::new(); + if let Some(v) = &limit { + query.push(("limit", v.to_string())); + } + + if let Some(v) = &page_token { + query.push(("page_token", v.to_string())); + } + + if let Some(v) = &sort_by { + query.push(("sort_by", v.to_string())); + } + + let request = self.client.get(url).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + #[doc = "List the current user's SSH public keys as a Stream\n\nSends repeated `GET` requests to `/session/me/sshkeys` until there are no more results.\n\nArguments:\n- `limit`: Maximum number of items returned by a single call\n- `sort_by`\n"] + pub fn sshkeys_get_stream<'a>( + &'a self, + limit: Option, + sort_by: Option, + ) -> impl futures::Stream>> + Unpin + '_ { + use futures::StreamExt; + use futures::TryFutureExt; + use futures::TryStreamExt; + self.sshkeys_get(limit, None, sort_by) + .map_ok(move |page| { + let page = page.into_inner(); + let first = futures::stream::iter(page.items.into_iter().map(Ok)); + let rest = futures::stream::try_unfold(page.next_page, move |state| async move { + if state.is_none() { + Ok(None) + } else { + self.sshkeys_get(None, state.as_deref(), None) + .map_ok(|page| { + let page = page.into_inner(); + Some(( + futures::stream::iter(page.items.into_iter().map(Ok)), + page.next_page, + )) + }) + .await + } + }) + .try_flatten(); + first.chain(rest) + }) + .try_flatten_stream() + .boxed() + } + + #[doc = "Create a new SSH public key for the current user\n\nSends a `POST` request to `/session/me/sshkeys`"] + pub async fn sshkeys_post<'a>( + &'a self, + body: &'a types::SshKeyCreate, + ) -> Result, Error> { + let url = format!("{}/session/me/sshkeys", self.baseurl,); + let request = self.client.post(url).json(body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + #[doc = "Get (by name) an SSH public key belonging to the current user\n\nSends a `GET` request to `/session/me/sshkeys/{ssh_key_name}`"] + pub async fn sshkeys_get_key<'a>( + &'a self, + ssh_key_name: &'a types::Name, + ) -> Result, Error> { + let url = format!( + "{}/session/me/sshkeys/{}", + self.baseurl, + progenitor_client::encode_path(&ssh_key_name.to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + #[doc = "Delete (by name) an SSH public key belonging to the current user\n\nSends a `DELETE` request to `/session/me/sshkeys/{ssh_key_name}`"] + pub async fn sshkeys_delete_key<'a>( + &'a self, + ssh_key_name: &'a types::Name, + ) -> Result, Error> { + let url = format!( + "{}/session/me/sshkeys/{}", + self.baseurl, + progenitor_client::encode_path(&ssh_key_name.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 204u16 => Ok(ResponseValue::empty(response)), + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + #[doc = "Sends a `GET` request to `/silos`\n\nArguments:\n- `limit`: Maximum number of items returned by a single call\n- `page_token`: Token returned by previous call to retreive the subsequent page\n- `sort_by`\n"] + pub async fn silos_get<'a>( + &'a self, + limit: Option, + page_token: Option<&'a str>, + sort_by: Option, + ) -> Result, Error> { + let url = format!("{}/silos", self.baseurl,); + let mut query = Vec::new(); + if let Some(v) = &limit { + query.push(("limit", v.to_string())); + } + + if let Some(v) = &page_token { + query.push(("page_token", v.to_string())); + } + + if let Some(v) = &sort_by { + query.push(("sort_by", v.to_string())); + } + + let request = self.client.get(url).query(&query).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + #[doc = "Sends repeated `GET` requests to `/silos` until there are no more results.\n\nArguments:\n- `limit`: Maximum number of items returned by a single call\n- `sort_by`\n"] + pub fn silos_get_stream<'a>( + &'a self, + limit: Option, + sort_by: Option, + ) -> impl futures::Stream>> + Unpin + '_ { + use futures::StreamExt; + use futures::TryFutureExt; + use futures::TryStreamExt; + self.silos_get(limit, None, sort_by) + .map_ok(move |page| { + let page = page.into_inner(); + let first = futures::stream::iter(page.items.into_iter().map(Ok)); + let rest = futures::stream::try_unfold(page.next_page, move |state| async move { + if state.is_none() { + Ok(None) + } else { + self.silos_get(None, state.as_deref(), None) + .map_ok(|page| { + let page = page.into_inner(); + Some(( + futures::stream::iter(page.items.into_iter().map(Ok)), + page.next_page, + )) + }) + .await + } + }) + .try_flatten(); + first.chain(rest) + }) + .try_flatten_stream() + .boxed() + } + + #[doc = "Create a new silo\n\nSends a `POST` request to `/silos`"] + pub async fn silos_post<'a>( + &'a self, + body: &'a types::SiloCreate, + ) -> Result, Error> { + let url = format!("{}/silos", self.baseurl,); + let request = self.client.post(url).json(body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 201u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + #[doc = "Fetch a specific silo\n\nSends a `GET` request to `/silos/{silo_name}`\n\nArguments:\n- `silo_name`: The silo's unique name.\n"] + pub async fn silos_get_silo<'a>( + &'a self, + silo_name: &'a types::Name, + ) -> Result, Error> { + let url = format!( + "{}/silos/{}", + self.baseurl, + progenitor_client::encode_path(&silo_name.to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + #[doc = "Delete a specific silo\n\nSends a `DELETE` request to `/silos/{silo_name}`\n\nArguments:\n- `silo_name`: The silo's unique name.\n"] + pub async fn silos_delete_silo<'a>( + &'a self, + silo_name: &'a types::Name, + ) -> Result, Error> { + let url = format!( + "{}/silos/{}", + self.baseurl, + progenitor_client::encode_path(&silo_name.to_string()), + ); + let request = self.client.delete(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 204u16 => Ok(ResponseValue::empty(response)), + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + #[doc = "Fetch the IAM policy for this Silo\n\nSends a `GET` request to `/silos/{silo_name}/policy`\n\nArguments:\n- `silo_name`: The silo's unique name.\n"] + pub async fn silos_get_silo_policy<'a>( + &'a self, + silo_name: &'a types::Name, + ) -> Result, Error> { + let url = format!( + "{}/silos/{}/policy", + self.baseurl, + progenitor_client::encode_path(&silo_name.to_string()), + ); + let request = self.client.get(url).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + + #[doc = "Update the IAM policy for this Silo\n\nSends a `PUT` request to `/silos/{silo_name}/policy`\n\nArguments:\n- `silo_name`: The silo's unique name.\n- `body`\n"] + pub async fn silos_put_silo_policy<'a>( + &'a self, + silo_name: &'a types::Name, + body: &'a types::SiloRolesPolicy, + ) -> Result, Error> { + let url = format!( + "{}/silos/{}/policy", + self.baseurl, + progenitor_client::encode_path(&silo_name.to_string()), + ); + let request = self.client.put(url).json(body).build()?; + let result = self.client.execute(request).await; + let response = result?; + match response.status().as_u16() { + 200u16 => ResponseValue::from_response(response).await, + 400u16..=499u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + 500u16..=599u16 => Err(Error::ErrorResponse( + ResponseValue::from_response(response).await?, + )), + _ => Err(Error::UnexpectedResponse(response)), + } + } + #[doc = "List all timeseries schema\n\nSends a `GET` request to `/timeseries/schema`\n\nArguments:\n- `limit`: Maximum number of items returned by a single call\n- `page_token`: Token returned by previous call to retreive the subsequent page\n"] pub async fn timeseries_schema_get<'a>( &'a self, diff --git a/sample_openapi/nexus.json b/sample_openapi/nexus.json index e14c22f..ce865b0 100644 --- a/sample_openapi/nexus.json +++ b/sample_openapi/nexus.json @@ -210,6 +210,172 @@ } } }, + "/images": { + "get": { + "tags": [ + "images:global" + ], + "summary": "List global images.", + "description": "Returns a list of all the global images. Global images are returned sorted by creation date, with the most recent images appearing first.", + "operationId": "images_get", + "parameters": [ + { + "in": "query", + "name": "limit", + "description": "Maximum number of items returned by a single call", + "schema": { + "nullable": true, + "type": "integer", + "format": "uint32", + "minimum": 1 + }, + "style": "form" + }, + { + "in": "query", + "name": "page_token", + "description": "Token returned by previous call to retreive the subsequent page", + "schema": { + "nullable": true, + "type": "string" + }, + "style": "form" + }, + { + "in": "query", + "name": "sort_by", + "schema": { + "$ref": "#/components/schemas/NameSortMode" + }, + "style": "form" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GlobalImageResultsPage" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + }, + "x-dropshot-pagination": true + }, + "post": { + "tags": [ + "images:global" + ], + "summary": "Create a global image.", + "description": "Create a new global image. This image can then be used by any user as a base for instances.", + "operationId": "images_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ImageCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "successful creation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GlobalImage" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/images/{image_name}": { + "get": { + "tags": [ + "images:global" + ], + "summary": "Get a global image.", + "description": "Returns the details of a specific global image.", + "operationId": "images_get_image", + "parameters": [ + { + "in": "path", + "name": "image_name", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GlobalImage" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "delete": { + "tags": [ + "images:global" + ], + "summary": "Delete a global image.", + "description": "Permanently delete a global image. This operation cannot be undone. Any instances using the global image will continue to run, however new instances can not be created with this image.", + "operationId": "images_delete_image", + "parameters": [ + { + "in": "path", + "name": "image_name", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + } + ], + "responses": { + "204": { + "description": "successful deletion" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, "/login": { "post": { "tags": [ @@ -467,6 +633,92 @@ } } }, + "/organizations/{organization_name}/policy": { + "get": { + "tags": [ + "organizations" + ], + "summary": "Fetch the IAM policy for this Organization", + "operationId": "organization_get_policy", + "parameters": [ + { + "in": "path", + "name": "organization_name", + "description": "The organization's unique name.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrganizationRolesPolicy" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "put": { + "tags": [ + "organizations" + ], + "summary": "Update the IAM policy for this Organization", + "operationId": "organization_put_policy", + "parameters": [ + { + "in": "path", + "name": "organization_name", + "description": "The organization's unique name.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrganizationRolesPolicy" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrganizationRolesPolicy" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, "/organizations/{organization_name}/projects": { "get": { "tags": [ @@ -970,6 +1222,250 @@ } } }, + "/organizations/{organization_name}/projects/{project_name}/images": { + "get": { + "tags": [ + "images" + ], + "summary": "List images", + "description": "List images in a project. The images are returned sorted by creation date, with the most recent images appearing first.", + "operationId": "project_images_get", + "parameters": [ + { + "in": "query", + "name": "limit", + "description": "Maximum number of items returned by a single call", + "schema": { + "nullable": true, + "type": "integer", + "format": "uint32", + "minimum": 1 + }, + "style": "form" + }, + { + "in": "query", + "name": "page_token", + "description": "Token returned by previous call to retreive the subsequent page", + "schema": { + "nullable": true, + "type": "string" + }, + "style": "form" + }, + { + "in": "query", + "name": "sort_by", + "schema": { + "$ref": "#/components/schemas/NameSortMode" + }, + "style": "form" + }, + { + "in": "path", + "name": "organization_name", + "description": "The organization's unique name.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + }, + { + "in": "path", + "name": "project_name", + "description": "The project's unique name within the organization.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ImageResultsPage" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + }, + "x-dropshot-pagination": true + }, + "post": { + "tags": [ + "images" + ], + "summary": "Create an image", + "description": "Create a new image in a project.", + "operationId": "project_images_post", + "parameters": [ + { + "in": "path", + "name": "organization_name", + "description": "The organization's unique name.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + }, + { + "in": "path", + "name": "project_name", + "description": "The project's unique name within the organization.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ImageCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "successful creation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Image" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/organizations/{organization_name}/projects/{project_name}/images/{image_name}": { + "get": { + "tags": [ + "images" + ], + "summary": "Get an image", + "description": "Get the details of a specific image in a project.", + "operationId": "project_images_get_image", + "parameters": [ + { + "in": "path", + "name": "image_name", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + }, + { + "in": "path", + "name": "organization_name", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + }, + { + "in": "path", + "name": "project_name", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Image" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "delete": { + "tags": [ + "images" + ], + "summary": "Delete an image", + "description": "Permanently delete an image from a project. This operation cannot be undone. Any instances in the project using the image will continue to run, however new instances can not be created with this image.", + "operationId": "project_images_delete_image", + "parameters": [ + { + "in": "path", + "name": "image_name", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + }, + { + "in": "path", + "name": "organization_name", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + }, + { + "in": "path", + "name": "project_name", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + } + ], + "responses": { + "204": { + "description": "successful deletion" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, "/organizations/{organization_name}/projects/{project_name}/instances": { "get": { "tags": [ @@ -1933,6 +2429,112 @@ } } }, + "/organizations/{organization_name}/projects/{project_name}/policy": { + "get": { + "tags": [ + "projects" + ], + "summary": "Fetch the IAM policy for this Project", + "operationId": "organization_projects_get_project_policy", + "parameters": [ + { + "in": "path", + "name": "organization_name", + "description": "The organization's unique name.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + }, + { + "in": "path", + "name": "project_name", + "description": "The project's unique name within the organization.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProjectRolesPolicy" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "put": { + "tags": [ + "projects" + ], + "summary": "Update the IAM policy for this Project", + "operationId": "organization_projects_put_project_policy", + "parameters": [ + { + "in": "path", + "name": "organization_name", + "description": "The organization's unique name.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + }, + { + "in": "path", + "name": "project_name", + "description": "The project's unique name within the organization.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProjectRolesPolicy" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProjectRolesPolicy" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, "/organizations/{organization_name}/projects/{project_name}/snapshots": { "get": { "tags": [ @@ -3773,6 +4375,68 @@ "x-dropshot-pagination": true } }, + "/policy": { + "get": { + "tags": [ + "policy" + ], + "summary": "Fetch the top-level IAM policy", + "operationId": "policy_get", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FleetRolesPolicy" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "put": { + "tags": [ + "policy" + ], + "summary": "Update the top-level IAM policy", + "operationId": "policy_put", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FleetRolesPolicy" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FleetRolesPolicy" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, "/roles": { "get": { "tags": [ @@ -3990,6 +4654,417 @@ } } }, + "/session/me/sshkeys": { + "get": { + "tags": [ + "sshkeys" + ], + "summary": "List the current user's SSH public keys", + "operationId": "sshkeys_get", + "parameters": [ + { + "in": "query", + "name": "limit", + "description": "Maximum number of items returned by a single call", + "schema": { + "nullable": true, + "type": "integer", + "format": "uint32", + "minimum": 1 + }, + "style": "form" + }, + { + "in": "query", + "name": "page_token", + "description": "Token returned by previous call to retreive the subsequent page", + "schema": { + "nullable": true, + "type": "string" + }, + "style": "form" + }, + { + "in": "query", + "name": "sort_by", + "schema": { + "$ref": "#/components/schemas/NameSortMode" + }, + "style": "form" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SshKeyResultsPage" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + }, + "x-dropshot-pagination": true + }, + "post": { + "tags": [ + "sshkeys" + ], + "summary": "Create a new SSH public key for the current user", + "operationId": "sshkeys_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SshKeyCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "successful creation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SshKey" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/session/me/sshkeys/{ssh_key_name}": { + "get": { + "tags": [ + "sshkeys" + ], + "summary": "Get (by name) an SSH public key belonging to the current user", + "operationId": "sshkeys_get_key", + "parameters": [ + { + "in": "path", + "name": "ssh_key_name", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SshKey" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "delete": { + "tags": [ + "sshkeys" + ], + "summary": "Delete (by name) an SSH public key belonging to the current user", + "operationId": "sshkeys_delete_key", + "parameters": [ + { + "in": "path", + "name": "ssh_key_name", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + } + ], + "responses": { + "204": { + "description": "successful deletion" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/silos": { + "get": { + "tags": [ + "silos" + ], + "operationId": "silos_get", + "parameters": [ + { + "in": "query", + "name": "limit", + "description": "Maximum number of items returned by a single call", + "schema": { + "nullable": true, + "type": "integer", + "format": "uint32", + "minimum": 1 + }, + "style": "form" + }, + { + "in": "query", + "name": "page_token", + "description": "Token returned by previous call to retreive the subsequent page", + "schema": { + "nullable": true, + "type": "string" + }, + "style": "form" + }, + { + "in": "query", + "name": "sort_by", + "schema": { + "$ref": "#/components/schemas/NameOrIdSortMode" + }, + "style": "form" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SiloResultsPage" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + }, + "x-dropshot-pagination": true + }, + "post": { + "tags": [ + "silos" + ], + "summary": "Create a new silo.", + "operationId": "silos_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SiloCreate" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "successful creation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Silo" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/silos/{silo_name}": { + "get": { + "tags": [ + "silos" + ], + "summary": "Fetch a specific silo", + "operationId": "silos_get_silo", + "parameters": [ + { + "in": "path", + "name": "silo_name", + "description": "The silo's unique name.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Silo" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "delete": { + "tags": [ + "silos" + ], + "summary": "Delete a specific silo.", + "operationId": "silos_delete_silo", + "parameters": [ + { + "in": "path", + "name": "silo_name", + "description": "The silo's unique name.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + } + ], + "responses": { + "204": { + "description": "successful deletion" + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/silos/{silo_name}/policy": { + "get": { + "tags": [ + "silos" + ], + "summary": "Fetch the IAM policy for this Silo", + "operationId": "silos_get_silo_policy", + "parameters": [ + { + "in": "path", + "name": "silo_name", + "description": "The silo's unique name.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SiloRolesPolicy" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + }, + "put": { + "tags": [ + "silos" + ], + "summary": "Update the IAM policy for this Silo", + "operationId": "silos_put_silo_policy", + "parameters": [ + { + "in": "path", + "name": "silo_name", + "description": "The silo's unique name.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Name" + }, + "style": "simple" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SiloRolesPolicy" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SiloRolesPolicy" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, "/timeseries/schema": { "get": { "tags": [ @@ -4176,6 +5251,15 @@ } }, "schemas": { + "BlockSize": { + "title": "disk block size in bytes", + "type": "integer", + "enum": [ + 512, + 2048, + 4096 + ] + }, "ByteCount": { "description": "A count of bytes, typically used either for memory or storage capacity\n\nThe maximum supported byte count is [`i64::MAX`]. This makes it somewhat inconvenient to define constructors: a u32 constructor can be infallible, but an i64 constructor can fail (if the value is negative) and a u64 constructor can fail (if the value is larger than i64::MAX). We provide all of these for consumers' convenience.", "type": "integer", @@ -4186,21 +5270,46 @@ "description": "The type of an individual datum of a metric.", "type": "string", "enum": [ - "Bool", - "I64", - "F64", - "String", - "Bytes", - "CumulativeI64", - "CumulativeF64", - "HistogramI64", - "HistogramF64" + "bool", + "i64", + "f64", + "string", + "bytes", + "cumulative_i64", + "cumulative_f64", + "histogram_i64", + "histogram_f64" + ] + }, + "Digest": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "sha256" + ] + }, + "value": { + "type": "string" + } + }, + "required": [ + "type", + "value" + ] + } ] }, "Disk": { "description": "Client view of an [`Disk`]", "type": "object", "properties": { + "block_size": { + "$ref": "#/components/schemas/ByteCount" + }, "description": { "description": "human-readable free-form text about a resource", "type": "string" @@ -4213,6 +5322,11 @@ "type": "string", "format": "uuid" }, + "image_id": { + "nullable": true, + "type": "string", + "format": "uuid" + }, "name": { "description": "unique, mutable, user-controlled identifier for each resource", "allOf": [ @@ -4248,6 +5362,7 @@ } }, "required": [ + "block_size", "description", "device_path", "id", @@ -4266,26 +5381,29 @@ "description": { "type": "string" }, + "disk_source": { + "description": "initial source for this disk", + "allOf": [ + { + "$ref": "#/components/schemas/DiskSource" + } + ] + }, "name": { "$ref": "#/components/schemas/Name" }, "size": { - "description": "size of the Disk", + "description": "total size of the Disk in bytes", "allOf": [ { "$ref": "#/components/schemas/ByteCount" } ] - }, - "snapshot_id": { - "nullable": true, - "description": "id for snapshot from which the Disk should be created, if any", - "type": "string", - "format": "uuid" } }, "required": [ "description", + "disk_source", "name", "size" ] @@ -4294,12 +5412,12 @@ "description": "Parameters for the [`Disk`](omicron_common::api::external::Disk) to be attached or detached to an instance", "type": "object", "properties": { - "disk": { + "name": { "$ref": "#/components/schemas/Name" } }, "required": [ - "disk" + "name" ] }, "DiskResultsPage": { @@ -4323,6 +5441,95 @@ "items" ] }, + "DiskSource": { + "description": "Different sources for a disk", + "oneOf": [ + { + "description": "Create a blank disk", + "type": "object", + "properties": { + "block_size": { + "description": "size of blocks for this Disk. valid values are: 512, 2048, or 4096", + "allOf": [ + { + "$ref": "#/components/schemas/BlockSize" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "blank" + ] + } + }, + "required": [ + "block_size", + "type" + ] + }, + { + "description": "Create a disk from a disk snapshot", + "type": "object", + "properties": { + "snapshot_id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "snapshot" + ] + } + }, + "required": [ + "snapshot_id", + "type" + ] + }, + { + "description": "Create a disk from a project image", + "type": "object", + "properties": { + "image_id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "image" + ] + } + }, + "required": [ + "image_id", + "type" + ] + }, + { + "description": "Create a disk from a global image", + "type": "object", + "properties": { + "image_id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "global_image" + ] + } + }, + "required": [ + "image_id", + "type" + ] + } + ] + }, "DiskState": { "description": "State of a Disk (primarily: attached or not)", "oneOf": [ @@ -4491,19 +5698,349 @@ "description": "The source from which a field is derived, the target or metric.", "type": "string", "enum": [ - "Target", - "Metric" + "target", + "metric" ] }, "FieldType": { "description": "The `FieldType` identifies the data type of a target or metric field.", "type": "string", "enum": [ - "String", - "I64", - "IpAddr", - "Uuid", - "Bool" + "string", + "i64", + "ip_addr", + "uuid", + "bool" + ] + }, + "FleetRoles": { + "type": "string", + "enum": [ + "admin", + "collaborator", + "viewer" + ] + }, + "FleetRolesPolicy": { + "description": "Client view of a [`Policy`], which describes how this resource may be accessed\n\nNote that the Policy only describes access granted explicitly for this resource. The policies of parent resources can also cause a user to have access to this resource.", + "type": "object", + "properties": { + "role_assignments": { + "description": "Roles directly assigned on this resource", + "type": "array", + "items": { + "$ref": "#/components/schemas/FleetRolesRoleAssignment" + } + } + }, + "required": [ + "role_assignments" + ] + }, + "FleetRolesRoleAssignment": { + "description": "Describes the assignment of a particular role on a particular resource to a particular identity (user, group, etc.)\n\nThe resource is not part of this structure. Rather, [`RoleAssignment`]s are put into a [`Policy`] and that Policy is applied to a particular resource.", + "type": "object", + "properties": { + "identity_id": { + "type": "string", + "format": "uuid" + }, + "identity_type": { + "$ref": "#/components/schemas/IdentityType" + }, + "role_name": { + "$ref": "#/components/schemas/FleetRoles" + } + }, + "required": [ + "identity_id", + "identity_type", + "role_name" + ] + }, + "GlobalImage": { + "description": "Client view of global Images", + "type": "object", + "properties": { + "block_size": { + "description": "size of blocks in bytes", + "allOf": [ + { + "$ref": "#/components/schemas/ByteCount" + } + ] + }, + "description": { + "description": "human-readable free-form text about a resource", + "type": "string" + }, + "digest": { + "nullable": true, + "description": "Hash of the image contents, if applicable", + "allOf": [ + { + "$ref": "#/components/schemas/Digest" + } + ] + }, + "id": { + "description": "unique, immutable, system-controlled identifier for each resource", + "type": "string", + "format": "uuid" + }, + "name": { + "description": "unique, mutable, user-controlled identifier for each resource", + "allOf": [ + { + "$ref": "#/components/schemas/Name" + } + ] + }, + "size": { + "description": "total size in bytes", + "allOf": [ + { + "$ref": "#/components/schemas/ByteCount" + } + ] + }, + "time_created": { + "description": "timestamp when this resource was created", + "type": "string", + "format": "date-time" + }, + "time_modified": { + "description": "timestamp when this resource was last modified", + "type": "string", + "format": "date-time" + }, + "url": { + "nullable": true, + "description": "URL source of this image, if any", + "type": "string" + }, + "version": { + "nullable": true, + "description": "Version of this, if any", + "type": "string" + } + }, + "required": [ + "block_size", + "description", + "id", + "name", + "size", + "time_created", + "time_modified" + ] + }, + "GlobalImageResultsPage": { + "description": "A single page of results", + "type": "object", + "properties": { + "items": { + "description": "list of items on this page of results", + "type": "array", + "items": { + "$ref": "#/components/schemas/GlobalImage" + } + }, + "next_page": { + "nullable": true, + "description": "token used to fetch the next page of results (if any)", + "type": "string" + } + }, + "required": [ + "items" + ] + }, + "IdentityType": { + "description": "Describes what kind of identity is described by an id", + "type": "string", + "enum": [ + "silo_user" + ] + }, + "Image": { + "description": "Client view of project Images", + "type": "object", + "properties": { + "block_size": { + "description": "size of blocks in bytes", + "allOf": [ + { + "$ref": "#/components/schemas/ByteCount" + } + ] + }, + "description": { + "description": "human-readable free-form text about a resource", + "type": "string" + }, + "digest": { + "nullable": true, + "description": "Hash of the image contents, if applicable", + "allOf": [ + { + "$ref": "#/components/schemas/Digest" + } + ] + }, + "id": { + "description": "unique, immutable, system-controlled identifier for each resource", + "type": "string", + "format": "uuid" + }, + "name": { + "description": "unique, mutable, user-controlled identifier for each resource", + "allOf": [ + { + "$ref": "#/components/schemas/Name" + } + ] + }, + "project_id": { + "description": "The project the disk belongs to", + "type": "string", + "format": "uuid" + }, + "size": { + "description": "total size in bytes", + "allOf": [ + { + "$ref": "#/components/schemas/ByteCount" + } + ] + }, + "time_created": { + "description": "timestamp when this resource was created", + "type": "string", + "format": "date-time" + }, + "time_modified": { + "description": "timestamp when this resource was last modified", + "type": "string", + "format": "date-time" + }, + "url": { + "nullable": true, + "description": "URL source of this image, if any", + "type": "string" + }, + "version": { + "nullable": true, + "description": "Version of this, if any", + "type": "string" + } + }, + "required": [ + "block_size", + "description", + "id", + "name", + "project_id", + "size", + "time_created", + "time_modified" + ] + }, + "ImageCreate": { + "description": "Create-time parameters for an [`Image`](omicron_common::api::external::Image)", + "type": "object", + "properties": { + "block_size": { + "description": "block size in bytes", + "allOf": [ + { + "$ref": "#/components/schemas/BlockSize" + } + ] + }, + "description": { + "type": "string" + }, + "name": { + "$ref": "#/components/schemas/Name" + }, + "source": { + "description": "The source of the image's contents.", + "allOf": [ + { + "$ref": "#/components/schemas/ImageSource" + } + ] + } + }, + "required": [ + "block_size", + "description", + "name", + "source" + ] + }, + "ImageResultsPage": { + "description": "A single page of results", + "type": "object", + "properties": { + "items": { + "description": "list of items on this page of results", + "type": "array", + "items": { + "$ref": "#/components/schemas/Image" + } + }, + "next_page": { + "nullable": true, + "description": "token used to fetch the next page of results (if any)", + "type": "string" + } + }, + "required": [ + "items" + ] + }, + "ImageSource": { + "description": "The source of the underlying image.", + "oneOf": [ + { + "type": "object", + "properties": { + "src": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "url" + ] + } + }, + "required": [ + "src", + "type" + ] + }, + { + "type": "object", + "properties": { + "src": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "snapshot" + ] + } + }, + "required": [ + "src", + "type" + ] + } ] }, "Instance": { @@ -4597,6 +6134,14 @@ "description": { "type": "string" }, + "disks": { + "description": "The disks to be created or attached for this instance.", + "default": [], + "type": "array", + "items": { + "$ref": "#/components/schemas/InstanceDiskAttachment" + } + }, "hostname": { "type": "string" }, @@ -4611,11 +6156,19 @@ }, "network_interfaces": { "description": "The network interfaces to be created for this instance.", + "default": { + "type": "default" + }, "allOf": [ { "$ref": "#/components/schemas/InstanceNetworkInterfaceAttachment" } ] + }, + "user_data": { + "description": "User data for instance initialization systems (such as cloud-init). Must be a Base64-encoded string, as specified in RFC 4648 § 4 (+ and / characters with padding). Maximum 32 KiB unencoded data.", + "default": "", + "type": "string" } }, "required": [ @@ -4626,6 +6179,76 @@ "ncpus" ] }, + "InstanceDiskAttachment": { + "description": "Describe the instance's disks at creation time", + "oneOf": [ + { + "description": "During instance creation, create and attach disks", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "disk_source": { + "description": "initial source for this disk", + "allOf": [ + { + "$ref": "#/components/schemas/DiskSource" + } + ] + }, + "name": { + "$ref": "#/components/schemas/Name" + }, + "size": { + "description": "total size of the Disk in bytes", + "allOf": [ + { + "$ref": "#/components/schemas/ByteCount" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "create" + ] + } + }, + "required": [ + "description", + "disk_source", + "name", + "size", + "type" + ] + }, + { + "description": "During instance creation, attach this disk", + "type": "object", + "properties": { + "name": { + "description": "A disk name to attach", + "allOf": [ + { + "$ref": "#/components/schemas/Name" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "attach" + ] + } + }, + "required": [ + "name", + "type" + ] + } + ] + }, "InstanceMigrate": { "description": "Migration parameters for an [`Instance`](omicron_common::api::external::Instance)", "type": "object", @@ -4647,12 +6270,15 @@ "type": "object", "properties": { "params": { - "$ref": "#/components/schemas/InstanceNetworkInterfaceCreate" + "type": "array", + "items": { + "$ref": "#/components/schemas/NetworkInterfaceCreate" + } }, "type": { "type": "string", "enum": [ - "Create" + "create" ] } }, @@ -4668,7 +6294,7 @@ "type": { "type": "string", "enum": [ - "Default" + "default" ] } }, @@ -4683,7 +6309,7 @@ "type": { "type": "string", "enum": [ - "None" + "none" ] } }, @@ -4693,20 +6319,6 @@ } ] }, - "InstanceNetworkInterfaceCreate": { - "type": "object", - "properties": { - "params": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NetworkInterfaceCreate" - } - } - }, - "required": [ - "params" - ] - }, "InstanceResultsPage": { "description": "A single page of results", "type": "object", @@ -4745,35 +6357,27 @@ ] }, "IpNet": { - "description": "An `IpNet` represents an IP network, either IPv4 or IPv6.", "oneOf": [ { - "type": "object", - "properties": { - "V4": { + "title": "v4", + "allOf": [ + { "$ref": "#/components/schemas/Ipv4Net" } - }, - "required": [ - "V4" - ], - "additionalProperties": false + ] }, { - "type": "object", - "properties": { - "V6": { + "title": "v6", + "allOf": [ + { "$ref": "#/components/schemas/Ipv6Net" } - }, - "required": [ - "V6" - ], - "additionalProperties": false + ] } ] }, "Ipv4Net": { + "example": "192.168.1.0/24", "title": "An IPv4 subnet", "description": "An IPv4 subnet, including prefix and subnet mask", "type": "string", @@ -4781,6 +6385,7 @@ "maxLength": 18 }, "Ipv6Net": { + "example": "fd12:3456::/64", "title": "An IPv6 subnet", "description": "An IPv6 subnet, including prefix and subnet mask", "type": "string", @@ -4788,6 +6393,7 @@ "maxLength": 43 }, "L4PortRange": { + "example": "22", "title": "A range of IP ports", "description": "An inclusive-inclusive range of IP ports. The second port may be omitted to represent a single port", "type": "string", @@ -4807,6 +6413,7 @@ ] }, "MacAddr": { + "example": "ff:ff:ff:ff:ff:ff", "title": "A MAC address", "description": "A Media Access Control address, in EUI-48 format", "type": "string", @@ -5032,6 +6639,50 @@ "items" ] }, + "OrganizationRoles": { + "type": "string", + "enum": [ + "admin", + "collaborator" + ] + }, + "OrganizationRolesPolicy": { + "description": "Client view of a [`Policy`], which describes how this resource may be accessed\n\nNote that the Policy only describes access granted explicitly for this resource. The policies of parent resources can also cause a user to have access to this resource.", + "type": "object", + "properties": { + "role_assignments": { + "description": "Roles directly assigned on this resource", + "type": "array", + "items": { + "$ref": "#/components/schemas/OrganizationRolesRoleAssignment" + } + } + }, + "required": [ + "role_assignments" + ] + }, + "OrganizationRolesRoleAssignment": { + "description": "Describes the assignment of a particular role on a particular resource to a particular identity (user, group, etc.)\n\nThe resource is not part of this structure. Rather, [`RoleAssignment`]s are put into a [`Policy`] and that Policy is applied to a particular resource.", + "type": "object", + "properties": { + "identity_id": { + "type": "string", + "format": "uuid" + }, + "identity_type": { + "$ref": "#/components/schemas/IdentityType" + }, + "role_name": { + "$ref": "#/components/schemas/OrganizationRoles" + } + }, + "required": [ + "identity_id", + "identity_type", + "role_name" + ] + }, "OrganizationUpdate": { "description": "Updateable properties of an [`Organization`](crate::external_api::views::Organization)", "type": "object", @@ -5132,6 +6783,51 @@ "items" ] }, + "ProjectRoles": { + "type": "string", + "enum": [ + "admin", + "collaborator", + "viewer" + ] + }, + "ProjectRolesPolicy": { + "description": "Client view of a [`Policy`], which describes how this resource may be accessed\n\nNote that the Policy only describes access granted explicitly for this resource. The policies of parent resources can also cause a user to have access to this resource.", + "type": "object", + "properties": { + "role_assignments": { + "description": "Roles directly assigned on this resource", + "type": "array", + "items": { + "$ref": "#/components/schemas/ProjectRolesRoleAssignment" + } + } + }, + "required": [ + "role_assignments" + ] + }, + "ProjectRolesRoleAssignment": { + "description": "Describes the assignment of a particular role on a particular resource to a particular identity (user, group, etc.)\n\nThe resource is not part of this structure. Rather, [`RoleAssignment`]s are put into a [`Policy`] and that Policy is applied to a particular resource.", + "type": "object", + "properties": { + "identity_id": { + "type": "string", + "format": "uuid" + }, + "identity_type": { + "$ref": "#/components/schemas/IdentityType" + }, + "role_name": { + "$ref": "#/components/schemas/ProjectRoles" + } + }, + "required": [ + "identity_id", + "identity_type", + "role_name" + ] + }, "ProjectUpdate": { "description": "Updateable properties of a [`Project`](crate::external_api::views::Project)", "type": "object", @@ -5470,11 +7166,6 @@ } ] }, - "router_id": { - "description": "The VPC Router to which the route belongs.", - "type": "string", - "format": "uuid" - }, "target": { "$ref": "#/components/schemas/RouteTarget" }, @@ -5487,6 +7178,11 @@ "description": "timestamp when this resource was last modified", "type": "string", "format": "date-time" + }, + "vpc_router_id": { + "description": "The VPC Router to which the route belongs.", + "type": "string", + "format": "uuid" } }, "required": [ @@ -5495,10 +7191,10 @@ "id", "kind", "name", - "router_id", "target", "time_created", - "time_modified" + "time_modified", + "vpc_router_id" ] }, "RouterRouteCreateParams": { @@ -5776,6 +7472,137 @@ "id" ] }, + "Silo": { + "description": "Client view of a ['Silo']", + "type": "object", + "properties": { + "description": { + "description": "human-readable free-form text about a resource", + "type": "string" + }, + "discoverable": { + "description": "A silo where discoverable is false can be retrieved only by its id - it will not be part of the \"list all silos\" output.", + "type": "boolean" + }, + "id": { + "description": "unique, immutable, system-controlled identifier for each resource", + "type": "string", + "format": "uuid" + }, + "name": { + "description": "unique, mutable, user-controlled identifier for each resource", + "allOf": [ + { + "$ref": "#/components/schemas/Name" + } + ] + }, + "time_created": { + "description": "timestamp when this resource was created", + "type": "string", + "format": "date-time" + }, + "time_modified": { + "description": "timestamp when this resource was last modified", + "type": "string", + "format": "date-time" + } + }, + "required": [ + "description", + "discoverable", + "id", + "name", + "time_created", + "time_modified" + ] + }, + "SiloCreate": { + "description": "Create-time parameters for a [`Silo`](crate::external_api::views::Silo)", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "discoverable": { + "type": "boolean" + }, + "name": { + "$ref": "#/components/schemas/Name" + } + }, + "required": [ + "description", + "discoverable", + "name" + ] + }, + "SiloResultsPage": { + "description": "A single page of results", + "type": "object", + "properties": { + "items": { + "description": "list of items on this page of results", + "type": "array", + "items": { + "$ref": "#/components/schemas/Silo" + } + }, + "next_page": { + "nullable": true, + "description": "token used to fetch the next page of results (if any)", + "type": "string" + } + }, + "required": [ + "items" + ] + }, + "SiloRoles": { + "type": "string", + "enum": [ + "admin", + "collaborator", + "viewer" + ] + }, + "SiloRolesPolicy": { + "description": "Client view of a [`Policy`], which describes how this resource may be accessed\n\nNote that the Policy only describes access granted explicitly for this resource. The policies of parent resources can also cause a user to have access to this resource.", + "type": "object", + "properties": { + "role_assignments": { + "description": "Roles directly assigned on this resource", + "type": "array", + "items": { + "$ref": "#/components/schemas/SiloRolesRoleAssignment" + } + } + }, + "required": [ + "role_assignments" + ] + }, + "SiloRolesRoleAssignment": { + "description": "Describes the assignment of a particular role on a particular resource to a particular identity (user, group, etc.)\n\nThe resource is not part of this structure. Rather, [`RoleAssignment`]s are put into a [`Policy`] and that Policy is applied to a particular resource.", + "type": "object", + "properties": { + "identity_id": { + "type": "string", + "format": "uuid" + }, + "identity_type": { + "$ref": "#/components/schemas/IdentityType" + }, + "role_name": { + "$ref": "#/components/schemas/SiloRoles" + } + }, + "required": [ + "identity_id", + "identity_type", + "role_name" + ] + }, "Sled": { "description": "Client view of an [`Sled`]", "type": "object", @@ -5941,6 +7768,99 @@ "items" ] }, + "SshKey": { + "description": "Client view of a [`SshKey`]", + "type": "object", + "properties": { + "description": { + "description": "human-readable free-form text about a resource", + "type": "string" + }, + "id": { + "description": "unique, immutable, system-controlled identifier for each resource", + "type": "string", + "format": "uuid" + }, + "name": { + "description": "unique, mutable, user-controlled identifier for each resource", + "allOf": [ + { + "$ref": "#/components/schemas/Name" + } + ] + }, + "public_key": { + "description": "SSH public key, e.g., `\"ssh-ed25519 AAAAC3NzaC...\"`", + "type": "string" + }, + "silo_user_id": { + "description": "The user to whom this key belongs", + "type": "string", + "format": "uuid" + }, + "time_created": { + "description": "timestamp when this resource was created", + "type": "string", + "format": "date-time" + }, + "time_modified": { + "description": "timestamp when this resource was last modified", + "type": "string", + "format": "date-time" + } + }, + "required": [ + "description", + "id", + "name", + "public_key", + "silo_user_id", + "time_created", + "time_modified" + ] + }, + "SshKeyCreate": { + "description": "Create-time parameters for an [`SshKey`](crate::external_api::views::SshKey)", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "name": { + "$ref": "#/components/schemas/Name" + }, + "public_key": { + "description": "SSH public key, e.g., `\"ssh-ed25519 AAAAC3NzaC...\"`", + "type": "string" + } + }, + "required": [ + "description", + "name", + "public_key" + ] + }, + "SshKeyResultsPage": { + "description": "A single page of results", + "type": "object", + "properties": { + "items": { + "description": "list of items on this page of results", + "type": "array", + "items": { + "$ref": "#/components/schemas/SshKey" + } + }, + "next_page": { + "nullable": true, + "description": "token used to fetch the next page of results (if any)", + "type": "string" + } + }, + "required": [ + "items" + ] + }, "TimeseriesName": { "title": "The name of a timeseries", "description": "Names are constructed by concatenating the target and metric names with ':'. Target and metric names must be lowercase alphanumeric characters with '_' separating words.", @@ -6878,22 +8798,6 @@ "nullable": true, "type": "string" }, - "ipv4_block": { - "nullable": true, - "allOf": [ - { - "$ref": "#/components/schemas/Ipv4Net" - } - ] - }, - "ipv6_block": { - "nullable": true, - "allOf": [ - { - "$ref": "#/components/schemas/Ipv6Net" - } - ] - }, "name": { "nullable": true, "allOf": [ @@ -6934,23 +8838,23 @@ "description": "Supported set of sort modes for scanning by id only.\n\nCurrently, we only support scanning in ascending order.", "type": "string", "enum": [ - "id-ascending" - ] - }, - "NameOrIdSortMode": { - "description": "Supported set of sort modes for scanning by name or id", - "type": "string", - "enum": [ - "name-ascending", - "name-descending", - "id-ascending" + "id_ascending" ] }, "NameSortMode": { "description": "Supported set of sort modes for scanning by name only\n\nCurrently, we only support scanning in ascending order.", "type": "string", "enum": [ - "name-ascending" + "name_ascending" + ] + }, + "NameOrIdSortMode": { + "description": "Supported set of sort modes for scanning by name or id", + "type": "string", + "enum": [ + "name_ascending", + "name_descending", + "id_ascending" ] } } @@ -6977,6 +8881,20 @@ "url": "http://oxide.computer/docs/#xxx" } }, + { + "name": "images", + "description": "Images are read-only Virtual Disks that may be used to boot Virtual Machines", + "externalDocs": { + "url": "http://oxide.computer/docs/#xxx" + } + }, + { + "name": "images:global", + "description": "Images are read-only Virtual Disks that may be used to boot Virtual Machines. These images are scoped globally.", + "externalDocs": { + "url": "http://oxide.computer/docs/#xxx" + } + }, { "name": "instances", "description": "Virtual machine instances are the basic unit of computation. These operations are used for provisioning, controlling, and destroying instances.", @@ -6998,6 +8916,13 @@ "url": "http://oxide.computer/docs/#xxx" } }, + { + "name": "policy", + "description": "System-wide IAM policy", + "externalDocs": { + "url": "http://oxide.computer/docs/#xxx" + } + }, { "name": "projects", "description": "Projects are a grouping of associated resources such as instances and disks within an organization for purposes of billing and access control.", @@ -7040,6 +8965,13 @@ "url": "http://oxide.computer/docs/#xxx" } }, + { + "name": "silos", + "description": "Silos represent a logical partition of users and resources.", + "externalDocs": { + "url": "http://oxide.computer/docs/#xxx" + } + }, { "name": "sleds", "description": "This tag should be moved into hardware", @@ -7054,6 +8986,13 @@ "url": "http://oxide.computer/docs/#xxx" } }, + { + "name": "sshkeys", + "description": "Public SSH keys for an individual user", + "externalDocs": { + "url": "http://oxide.computer/docs/#xxx" + } + }, { "name": "subnets", "description": "This tag should be moved into a generic network tag",