From cf074a33264d555b3647091f4b74e61c5ff33f12 Mon Sep 17 00:00:00 2001 From: Sean Klein Date: Thu, 14 Dec 2023 14:48:24 -0800 Subject: [PATCH 1/3] Remove lazy_static in favor of once_cell --- Cargo.lock | 7 +- Cargo.toml | 1 - common/Cargo.toml | 2 +- common/src/address.rs | 108 +- nexus/Cargo.toml | 1 - nexus/db-queries/Cargo.toml | 2 +- nexus/db-queries/src/authn/external/spoof.rs | 29 +- nexus/db-queries/src/authz/api_resources.rs | 7 +- .../db-queries/src/db/datastore/silo_user.rs | 10 +- nexus/db-queries/src/db/fixed_data/mod.rs | 15 +- nexus/db-queries/src/db/fixed_data/project.rs | 20 +- .../src/db/fixed_data/role_assignment.rs | 13 +- .../src/db/fixed_data/role_builtin.rs | 34 +- nexus/db-queries/src/db/fixed_data/silo.rs | 104 +- .../db-queries/src/db/fixed_data/silo_user.rs | 54 +- .../src/db/fixed_data/user_builtin.rs | 106 +- nexus/db-queries/src/db/fixed_data/vpc.rs | 42 +- .../src/db/fixed_data/vpc_firewall_rule.rs | 73 +- .../src/db/fixed_data/vpc_subnet.rs | 53 +- .../src/db/queries/network_interface.rs | 60 +- nexus/db-queries/src/db/saga_recovery.rs | 12 +- nexus/defaults/Cargo.toml | 2 +- nexus/defaults/src/lib.rs | 88 +- nexus/preprocessed_configs/config.xml | 41 + nexus/src/external_api/console_api.rs | 12 +- nexus/tests/integration_tests/endpoints.rs | 3757 +++++++++-------- nexus/tests/integration_tests/unauthorized.rs | 32 +- 27 files changed, 2450 insertions(+), 2235 deletions(-) create mode 100644 nexus/preprocessed_configs/config.xml diff --git a/Cargo.lock b/Cargo.lock index e9e5c1594d..2889c5878c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4164,7 +4164,6 @@ dependencies = [ "internal-dns", "ipnetwork", "itertools 0.12.0", - "lazy_static", "macaddr", "newtype_derive", "nexus-db-model", @@ -4177,6 +4176,7 @@ dependencies = [ "omicron-sled-agent", "omicron-test-utils", "omicron-workspace-hack", + "once_cell", "openapiv3", "openssl", "oso", @@ -4213,9 +4213,9 @@ name = "nexus-defaults" version = "0.1.0" dependencies = [ "ipnetwork", - "lazy_static", "omicron-common", "omicron-workspace-hack", + "once_cell", "rand 0.8.5", "serde_json", ] @@ -4618,10 +4618,10 @@ dependencies = [ "hex", "http", "ipnetwork", - "lazy_static", "libc", "macaddr", "omicron-workspace-hack", + "once_cell", "parse-display", "progenitor", "proptest", @@ -4771,7 +4771,6 @@ dependencies = [ "internal-dns", "ipnetwork", "itertools 0.12.0", - "lazy_static", "macaddr", "mg-admin-client", "mime_guess", diff --git a/Cargo.toml b/Cargo.toml index 841c7bb16b..6566cc0ed3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -229,7 +229,6 @@ ipnetwork = { version = "0.20", features = ["schemars"] } itertools = "0.12.0" key-manager = { path = "key-manager" } kstat-rs = "0.2.3" -lazy_static = "1.4.0" libc = "0.2.151" linear-map = "1.2.0" macaddr = { version = "1.0.1", features = ["serde_std"] } diff --git a/common/Cargo.toml b/common/Cargo.toml index 49997e619c..3941f5303e 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -17,7 +17,6 @@ hex.workspace = true http.workspace = true ipnetwork.workspace = true macaddr.workspace = true -lazy_static.workspace = true proptest = { workspace = true, optional = true } rand.workspace = true reqwest = { workspace = true, features = ["rustls-tls", "stream"] } @@ -38,6 +37,7 @@ uuid.workspace = true parse-display.workspace = true progenitor.workspace = true omicron-workspace-hack.workspace = true +once_cell.workspace = true [dev-dependencies] camino-tempfile.workspace = true diff --git a/common/src/address.rs b/common/src/address.rs index 992e8f0406..94361a2705 100644 --- a/common/src/address.rs +++ b/common/src/address.rs @@ -9,6 +9,7 @@ use crate::api::external::{self, Error, Ipv4Net, Ipv6Net}; use ipnetwork::{Ipv4Network, Ipv6Network}; +use once_cell::sync::Lazy; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV6}; @@ -76,65 +77,78 @@ pub const NTP_PORT: u16 = 123; // that situation (which may be as soon as allocating ephemeral IPs). pub const NUM_SOURCE_NAT_PORTS: u16 = 1 << 14; -lazy_static::lazy_static! { - // Services that require external connectivity are given an OPTE port - // with a "Service VNIC" record. Like a "Guest VNIC", a service is - // placed within a VPC (a built-in services VPC), along with a VPC subnet. - // But unlike guest instances which are created at runtime by Nexus, these - // services are created by RSS early on. So, we have some fixed values - // used to bootstrap service OPTE ports. Each service kind uses a distinct - // VPC subnet which RSS will allocate addresses from for those services. - // The specific values aren't deployment-specific as they are virtualized - // within OPTE. - - /// The IPv6 prefix assigned to the built-in services VPC. - // The specific prefix here was randomly chosen from the expected VPC - // prefix range (`fd00::/48`). See `random_vpc_ipv6_prefix`. - // Furthermore, all the below *_OPTE_IPV6_SUBNET constants are - // /64's within this prefix. - pub static ref SERVICE_VPC_IPV6_PREFIX: Ipv6Net = Ipv6Net( +// Services that require external connectivity are given an OPTE port +// with a "Service VNIC" record. Like a "Guest VNIC", a service is +// placed within a VPC (a built-in services VPC), along with a VPC subnet. +// But unlike guest instances which are created at runtime by Nexus, these +// services are created by RSS early on. So, we have some fixed values +// used to bootstrap service OPTE ports. Each service kind uses a distinct +// VPC subnet which RSS will allocate addresses from for those services. +// The specific values aren't deployment-specific as they are virtualized +// within OPTE. + +/// The IPv6 prefix assigned to the built-in services VPC. +// The specific prefix here was randomly chosen from the expected VPC +// prefix range (`fd00::/48`). See `random_vpc_ipv6_prefix`. +// Furthermore, all the below *_OPTE_IPV6_SUBNET constants are +// /64's within this prefix. +pub static SERVICE_VPC_IPV6_PREFIX: Lazy = Lazy::new(|| { + Ipv6Net( Ipv6Network::new( Ipv6Addr::new(0xfd77, 0xe9d2, 0x9cd9, 0, 0, 0, 0, 0), Ipv6Net::VPC_IPV6_PREFIX_LENGTH, - ).unwrap(), - ); - - /// The IPv4 subnet for External DNS OPTE ports. - pub static ref DNS_OPTE_IPV4_SUBNET: Ipv4Net = - Ipv4Net(Ipv4Network::new(Ipv4Addr::new(172, 30, 1, 0), 24).unwrap()); - - /// The IPv6 subnet for External DNS OPTE ports. - pub static ref DNS_OPTE_IPV6_SUBNET: Ipv6Net = Ipv6Net( + ) + .unwrap(), + ) +}); + +/// The IPv4 subnet for External DNS OPTE ports. +pub static DNS_OPTE_IPV4_SUBNET: Lazy = Lazy::new(|| { + Ipv4Net(Ipv4Network::new(Ipv4Addr::new(172, 30, 1, 0), 24).unwrap()) +}); + +/// The IPv6 subnet for External DNS OPTE ports. +pub static DNS_OPTE_IPV6_SUBNET: Lazy = Lazy::new(|| { + Ipv6Net( Ipv6Network::new( Ipv6Addr::new(0xfd77, 0xe9d2, 0x9cd9, 1, 0, 0, 0, 0), Ipv6Net::VPC_SUBNET_IPV6_PREFIX_LENGTH, - ).unwrap(), - ); - - /// The IPv4 subnet for Nexus OPTE ports. - pub static ref NEXUS_OPTE_IPV4_SUBNET: Ipv4Net = - Ipv4Net(Ipv4Network::new(Ipv4Addr::new(172, 30, 2, 0), 24).unwrap()); - - /// The IPv6 subnet for Nexus OPTE ports. - pub static ref NEXUS_OPTE_IPV6_SUBNET: Ipv6Net = Ipv6Net( + ) + .unwrap(), + ) +}); + +/// The IPv4 subnet for Nexus OPTE ports. +pub static NEXUS_OPTE_IPV4_SUBNET: Lazy = Lazy::new(|| { + Ipv4Net(Ipv4Network::new(Ipv4Addr::new(172, 30, 2, 0), 24).unwrap()) +}); + +/// The IPv6 subnet for Nexus OPTE ports. +pub static NEXUS_OPTE_IPV6_SUBNET: Lazy = Lazy::new(|| { + Ipv6Net( Ipv6Network::new( Ipv6Addr::new(0xfd77, 0xe9d2, 0x9cd9, 2, 0, 0, 0, 0), Ipv6Net::VPC_SUBNET_IPV6_PREFIX_LENGTH, - ).unwrap(), - ); - - /// The IPv4 subnet for Boundary NTP OPTE ports. - pub static ref NTP_OPTE_IPV4_SUBNET: Ipv4Net = - Ipv4Net(Ipv4Network::new(Ipv4Addr::new(172, 30, 3, 0), 24).unwrap()); - - /// The IPv6 subnet for Boundary NTP OPTE ports. - pub static ref NTP_OPTE_IPV6_SUBNET: Ipv6Net = Ipv6Net( + ) + .unwrap(), + ) +}); + +/// The IPv4 subnet for Boundary NTP OPTE ports. +pub static NTP_OPTE_IPV4_SUBNET: Lazy = Lazy::new(|| { + Ipv4Net(Ipv4Network::new(Ipv4Addr::new(172, 30, 3, 0), 24).unwrap()) +}); + +/// The IPv6 subnet for Boundary NTP OPTE ports. +pub static NTP_OPTE_IPV6_SUBNET: Lazy = Lazy::new(|| { + Ipv6Net( Ipv6Network::new( Ipv6Addr::new(0xfd77, 0xe9d2, 0x9cd9, 3, 0, 0, 0, 0), Ipv6Net::VPC_SUBNET_IPV6_PREFIX_LENGTH, - ).unwrap(), - ); -} + ) + .unwrap(), + ) +}); // Anycast is a mechanism in which a single IP address is shared by multiple // devices, and the destination is located based on routing distance. diff --git a/nexus/Cargo.toml b/nexus/Cargo.toml index 704a7ab7bd..25833ec104 100644 --- a/nexus/Cargo.toml +++ b/nexus/Cargo.toml @@ -32,7 +32,6 @@ http.workspace = true hyper.workspace = true internal-dns.workspace = true ipnetwork.workspace = true -lazy_static.workspace = true macaddr.workspace = true mime_guess.workspace = true # Not under "dev-dependencies"; these also need to be implemented for diff --git a/nexus/db-queries/Cargo.toml b/nexus/db-queries/Cargo.toml index 9d8afd1fea..d5320be733 100644 --- a/nexus/db-queries/Cargo.toml +++ b/nexus/db-queries/Cargo.toml @@ -24,9 +24,9 @@ headers.workspace = true http.workspace = true hyper.workspace = true ipnetwork.workspace = true -lazy_static.workspace = true macaddr.workspace = true newtype_derive.workspace = true +once_cell.workspace = true openssl.workspace = true oso.workspace = true paste.workspace = true diff --git a/nexus/db-queries/src/authn/external/spoof.rs b/nexus/db-queries/src/authn/external/spoof.rs index 0b5896a6f8..9b5ed94bde 100644 --- a/nexus/db-queries/src/authn/external/spoof.rs +++ b/nexus/db-queries/src/authn/external/spoof.rs @@ -16,7 +16,7 @@ use anyhow::Context; use async_trait::async_trait; use headers::authorization::{Authorization, Bearer}; use headers::HeaderMapExt; -use lazy_static::lazy_static; +use once_cell::sync::Lazy; use uuid::Uuid; // This scheme is intended for demos, development, and testing until we have a @@ -54,18 +54,21 @@ const SPOOF_RESERVED_BAD_CREDS: &str = "this-fake-ID-it-is-truly-excellent"; // subsets of the base64 character set, so we do not bother encoding them. const SPOOF_PREFIX: &str = "oxide-spoof-"; -lazy_static! { - /// Actor (id) used for the special "bad credentials" error - static ref SPOOF_RESERVED_BAD_CREDS_ACTOR: Actor = Actor::UserBuiltin { - user_builtin_id: "22222222-2222-2222-2222-222222222222".parse().unwrap(), - }; - /// Complete HTTP header value to trigger the "bad actor" error - pub static ref SPOOF_HEADER_BAD_ACTOR: Authorization = - make_header_value_str(SPOOF_RESERVED_BAD_ACTOR).unwrap(); - /// Complete HTTP header value to trigger the "bad creds" error - pub static ref SPOOF_HEADER_BAD_CREDS: Authorization = - make_header_value_str(SPOOF_RESERVED_BAD_CREDS).unwrap(); -} +/// Actor (id) used for the special "bad credentials" error +static SPOOF_RESERVED_BAD_CREDS_ACTOR: Lazy = + Lazy::new(|| Actor::UserBuiltin { + user_builtin_id: "22222222-2222-2222-2222-222222222222" + .parse() + .unwrap(), + }); + +/// Complete HTTP header value to trigger the "bad actor" error +pub static SPOOF_HEADER_BAD_ACTOR: Lazy> = + Lazy::new(|| make_header_value_str(SPOOF_RESERVED_BAD_ACTOR).unwrap()); + +/// Complete HTTP header value to trigger the "bad creds" error +pub static SPOOF_HEADER_BAD_CREDS: Lazy> = + Lazy::new(|| make_header_value_str(SPOOF_RESERVED_BAD_CREDS).unwrap()); /// Implements a (test-only) authentication scheme where the client simply /// provides the actor information in a custom bearer token and we always trust diff --git a/nexus/db-queries/src/authz/api_resources.rs b/nexus/db-queries/src/authz/api_resources.rs index 2dfe2f7174..8485b8f11f 100644 --- a/nexus/db-queries/src/authz/api_resources.rs +++ b/nexus/db-queries/src/authz/api_resources.rs @@ -42,9 +42,9 @@ use crate::db::DataStore; use authz_macros::authz_resource; use futures::future::BoxFuture; use futures::FutureExt; -use lazy_static::lazy_static; use nexus_types::external_api::shared::{FleetRole, ProjectRole, SiloRole}; use omicron_common::api::external::{Error, LookupType, ResourceType}; +use once_cell::sync::Lazy; use oso::PolarClass; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -169,9 +169,8 @@ pub struct Fleet; /// Singleton representing the [`Fleet`] itself for authz purposes pub const FLEET: Fleet = Fleet; -lazy_static! { - pub static ref FLEET_LOOKUP: LookupType = LookupType::ById(*FLEET_ID); -} +pub static FLEET_LOOKUP: Lazy = + Lazy::new(|| LookupType::ById(*FLEET_ID)); impl Eq for Fleet {} impl PartialEq for Fleet { diff --git a/nexus/db-queries/src/db/datastore/silo_user.rs b/nexus/db-queries/src/db/datastore/silo_user.rs index 6084f8c2ab..59cb19a609 100644 --- a/nexus/db-queries/src/db/datastore/silo_user.rs +++ b/nexus/db-queries/src/db/datastore/silo_user.rs @@ -363,11 +363,11 @@ impl DataStore { let builtin_users = [ // Note: "db_init" is also a builtin user, but that one by necessity // is created with the database. - &*authn::USER_SERVICE_BALANCER, - &*authn::USER_INTERNAL_API, - &*authn::USER_INTERNAL_READ, - &*authn::USER_EXTERNAL_AUTHN, - &*authn::USER_SAGA_RECOVERY, + &authn::USER_SERVICE_BALANCER, + &authn::USER_INTERNAL_API, + &authn::USER_INTERNAL_READ, + &authn::USER_EXTERNAL_AUTHN, + &authn::USER_SAGA_RECOVERY, ] .iter() .map(|u| { diff --git a/nexus/db-queries/src/db/fixed_data/mod.rs b/nexus/db-queries/src/db/fixed_data/mod.rs index 5c91407134..4f896eb5d1 100644 --- a/nexus/db-queries/src/db/fixed_data/mod.rs +++ b/nexus/db-queries/src/db/fixed_data/mod.rs @@ -31,7 +31,7 @@ // 001de000-074c built-in services vpc // 001de000-c470 built-in services vpc subnets -use lazy_static::lazy_static; +use once_cell::sync::Lazy; pub mod project; pub mod role_assignment; @@ -43,13 +43,12 @@ pub mod vpc; pub mod vpc_firewall_rule; pub mod vpc_subnet; -lazy_static! { - /* See above for where this uuid comes from. */ - pub static ref FLEET_ID: uuid::Uuid = - "001de000-1334-4000-8000-000000000000" - .parse() - .expect("invalid uuid for builtin fleet id"); -} +/* See above for where this uuid comes from. */ +pub static FLEET_ID: Lazy = Lazy::new(|| { + "001de000-1334-4000-8000-000000000000" + .parse() + .expect("invalid uuid for builtin fleet id") +}); #[cfg(test)] fn assert_valid_uuid(id: &uuid::Uuid) { diff --git a/nexus/db-queries/src/db/fixed_data/project.rs b/nexus/db-queries/src/db/fixed_data/project.rs index 52450438c0..e240900e0c 100644 --- a/nexus/db-queries/src/db/fixed_data/project.rs +++ b/nexus/db-queries/src/db/fixed_data/project.rs @@ -4,18 +4,20 @@ use crate::db; use crate::db::datastore::SERVICES_DB_NAME; -use lazy_static::lazy_static; use nexus_types::external_api::params; use omicron_common::api::external::IdentityMetadataCreateParams; +use once_cell::sync::Lazy; -lazy_static! { - /// UUID of built-in project for internal services on the rack. - pub static ref SERVICES_PROJECT_ID: uuid::Uuid = "001de000-4401-4000-8000-000000000000" +/// UUID of built-in project for internal services on the rack. +pub static SERVICES_PROJECT_ID: Lazy = Lazy::new(|| { + "001de000-4401-4000-8000-000000000000" .parse() - .expect("invalid uuid for builtin services project id"); + .expect("invalid uuid for builtin services project id") +}); - /// Built-in Project for internal services on the rack. - pub static ref SERVICES_PROJECT: db::model::Project = db::model::Project::new_with_id( +/// Built-in Project for internal services on the rack. +pub static SERVICES_PROJECT: Lazy = Lazy::new(|| { + db::model::Project::new_with_id( *SERVICES_PROJECT_ID, *super::silo::INTERNAL_SILO_ID, params::ProjectCreate { @@ -24,5 +26,5 @@ lazy_static! { description: "Built-in project for Oxide Services".to_string(), }, }, - ); -} + ) +}); diff --git a/nexus/db-queries/src/db/fixed_data/role_assignment.rs b/nexus/db-queries/src/db/fixed_data/role_assignment.rs index 7d7ddffab6..d6c95d47b6 100644 --- a/nexus/db-queries/src/db/fixed_data/role_assignment.rs +++ b/nexus/db-queries/src/db/fixed_data/role_assignment.rs @@ -8,10 +8,10 @@ use super::user_builtin; use super::FLEET_ID; use crate::db::model::IdentityType; use crate::db::model::RoleAssignment; -use lazy_static::lazy_static; +use once_cell::sync::Lazy; -lazy_static! { - pub static ref BUILTIN_ROLE_ASSIGNMENTS: Vec = +pub static BUILTIN_ROLE_ASSIGNMENTS: Lazy> = + Lazy::new(|| { vec![ // The "internal-api" user gets the "admin" role on the sole Fleet. // This is a pretty elevated privilege. @@ -24,7 +24,6 @@ lazy_static! { *FLEET_ID, role_builtin::FLEET_ADMIN.role_name, ), - // The "USER_SERVICE_BALANCER" user gets the "admin" role on the // Fleet. // @@ -38,7 +37,6 @@ lazy_static! { *FLEET_ID, role_builtin::FLEET_ADMIN.role_name, ), - // The "internal-read" user gets the "viewer" role on the sole // Fleet. This will grant them the ability to read various control // plane data (like the list of sleds), which is in turn used to @@ -50,7 +48,6 @@ lazy_static! { *FLEET_ID, role_builtin::FLEET_VIEWER.role_name, ), - // The "external-authenticator" user gets the "authenticator" role // on the sole fleet. This grants them the ability to create // sessions. @@ -61,5 +58,5 @@ lazy_static! { *FLEET_ID, role_builtin::FLEET_AUTHENTICATOR.role_name, ), - ]; -} + ] + }); diff --git a/nexus/db-queries/src/db/fixed_data/role_builtin.rs b/nexus/db-queries/src/db/fixed_data/role_builtin.rs index 865f6328f4..f58077fc3f 100644 --- a/nexus/db-queries/src/db/fixed_data/role_builtin.rs +++ b/nexus/db-queries/src/db/fixed_data/role_builtin.rs @@ -3,8 +3,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. //! Built-in roles -use lazy_static::lazy_static; use omicron_common::api; +use once_cell::sync::Lazy; #[derive(Clone, Debug)] pub struct RoleBuiltinConfig { @@ -13,28 +13,36 @@ pub struct RoleBuiltinConfig { pub description: &'static str, } -lazy_static! { - pub static ref FLEET_ADMIN: RoleBuiltinConfig = RoleBuiltinConfig { +pub static FLEET_ADMIN: Lazy = + Lazy::new(|| RoleBuiltinConfig { resource_type: api::external::ResourceType::Fleet, role_name: "admin", description: "Fleet Administrator", - }; - pub static ref FLEET_AUTHENTICATOR: RoleBuiltinConfig = RoleBuiltinConfig { + }); + +pub static FLEET_AUTHENTICATOR: Lazy = + Lazy::new(|| RoleBuiltinConfig { resource_type: api::external::ResourceType::Fleet, role_name: "external-authenticator", description: "Fleet External Authenticator", - }; - pub static ref FLEET_VIEWER: RoleBuiltinConfig = RoleBuiltinConfig { + }); + +pub static FLEET_VIEWER: Lazy = + Lazy::new(|| RoleBuiltinConfig { resource_type: api::external::ResourceType::Fleet, role_name: "viewer", description: "Fleet Viewer", - }; - pub static ref SILO_ADMIN: RoleBuiltinConfig = RoleBuiltinConfig { + }); + +pub static SILO_ADMIN: Lazy = + Lazy::new(|| RoleBuiltinConfig { resource_type: api::external::ResourceType::Silo, role_name: "admin", description: "Silo Administrator", - }; - pub static ref BUILTIN_ROLES: Vec = vec![ + }); + +pub static BUILTIN_ROLES: Lazy> = Lazy::new(|| { + vec![ FLEET_ADMIN.clone(), FLEET_AUTHENTICATOR.clone(), FLEET_VIEWER.clone(), @@ -69,8 +77,8 @@ lazy_static! { role_name: "viewer", description: "Project Viewer", }, - ]; -} + ] +}); #[cfg(test)] mod test { diff --git a/nexus/db-queries/src/db/fixed_data/silo.rs b/nexus/db-queries/src/db/fixed_data/silo.rs index 6eba849ee3..62bcc61c1e 100644 --- a/nexus/db-queries/src/db/fixed_data/silo.rs +++ b/nexus/db-queries/src/db/fixed_data/silo.rs @@ -3,62 +3,66 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. use crate::db; -use lazy_static::lazy_static; use nexus_types::external_api::{params, shared}; use omicron_common::api::external::IdentityMetadataCreateParams; +use once_cell::sync::Lazy; -lazy_static! { - pub static ref SILO_ID: uuid::Uuid = "001de000-5110-4000-8000-000000000000" +pub static SILO_ID: Lazy = Lazy::new(|| { + "001de000-5110-4000-8000-000000000000" .parse() - .expect("invalid uuid for builtin silo id"); + .expect("invalid uuid for builtin silo id") +}); - /// "Default" Silo - /// - /// This was historically used for demos and the unit tests. The plan is to - /// remove it per omicron#2305. - pub static ref DEFAULT_SILO: db::model::Silo = - db::model::Silo::new_with_id( - *SILO_ID, - params::SiloCreate { - identity: IdentityMetadataCreateParams { - name: "default-silo".parse().unwrap(), - description: "default silo".to_string(), - }, - // This quota is actually _unused_ because the default silo - // isn't constructed in the same way a normal silo would be. - quotas: params::SiloQuotasCreate::empty(), - discoverable: false, - identity_mode: shared::SiloIdentityMode::LocalOnly, - admin_group_name: None, - tls_certificates: vec![], - mapped_fleet_roles: Default::default(), +/// "Default" Silo +/// +/// This was historically used for demos and the unit tests. The plan is to +/// remove it per omicron#2305. +pub static DEFAULT_SILO: Lazy = Lazy::new(|| { + db::model::Silo::new_with_id( + *SILO_ID, + params::SiloCreate { + identity: IdentityMetadataCreateParams { + name: "default-silo".parse().unwrap(), + description: "default silo".to_string(), }, - ) - .unwrap(); + // This quota is actually _unused_ because the default silo + // isn't constructed in the same way a normal silo would be. + quotas: params::SiloQuotasCreate::empty(), + discoverable: false, + identity_mode: shared::SiloIdentityMode::LocalOnly, + admin_group_name: None, + tls_certificates: vec![], + mapped_fleet_roles: Default::default(), + }, + ) + .unwrap() +}); - /// UUID of built-in internal silo. - pub static ref INTERNAL_SILO_ID: uuid::Uuid = - "001de000-5110-4000-8000-000000000001" - .parse() - .expect("invalid uuid for builtin silo id"); +/// UUID of built-in internal silo. +pub static INTERNAL_SILO_ID: Lazy = Lazy::new(|| { + "001de000-5110-4000-8000-000000000001" + .parse() + .expect("invalid uuid for builtin silo id") +}); - /// Built-in Silo to house internal resources. It contains no users and - /// can't be logged into. - pub static ref INTERNAL_SILO: db::model::Silo = - db::model::Silo::new_with_id( - *INTERNAL_SILO_ID, - params::SiloCreate { - identity: IdentityMetadataCreateParams { - name: "oxide-internal".parse().unwrap(), - description: "Built-in internal Silo.".to_string(), - }, - // The internal silo contains no virtual resources, so it has no allotted capacity. - quotas: params::SiloQuotasCreate::empty(), - discoverable: false, - identity_mode: shared::SiloIdentityMode::LocalOnly, - admin_group_name: None, - tls_certificates: vec![], - mapped_fleet_roles: Default::default(), +/// Built-in Silo to house internal resources. It contains no users and +/// can't be logged into. +pub static INTERNAL_SILO: Lazy = Lazy::new(|| { + db::model::Silo::new_with_id( + *INTERNAL_SILO_ID, + params::SiloCreate { + identity: IdentityMetadataCreateParams { + name: "oxide-internal".parse().unwrap(), + description: "Built-in internal Silo.".to_string(), }, - ).unwrap(); -} + // The internal silo contains no virtual resources, so it has no allotted capacity. + quotas: params::SiloQuotasCreate::empty(), + discoverable: false, + identity_mode: shared::SiloIdentityMode::LocalOnly, + admin_group_name: None, + tls_certificates: vec![], + mapped_fleet_roles: Default::default(), + }, + ) + .unwrap() +}); diff --git a/nexus/db-queries/src/db/fixed_data/silo_user.rs b/nexus/db-queries/src/db/fixed_data/silo_user.rs index d54bcfa59f..b5253b68e3 100644 --- a/nexus/db-queries/src/db/fixed_data/silo_user.rs +++ b/nexus/db-queries/src/db/fixed_data/silo_user.rs @@ -6,25 +6,26 @@ use super::role_builtin; use crate::db; use crate::db::identity::Asset; -use lazy_static::lazy_static; +use once_cell::sync::Lazy; -lazy_static! { - /// Test user that's granted all privileges, used for automated testing and - /// local development - // TODO-security Once we have a way to bootstrap the initial Silo with the - // initial privileged user, this user should be created in the test suite, - // not automatically at Nexus startup. See omicron#2305. - pub static ref USER_TEST_PRIVILEGED: db::model::SiloUser = - db::model::SiloUser::new( - *db::fixed_data::silo::SILO_ID, - // "4007" looks a bit like "root". - "001de000-05e4-4000-8000-000000004007".parse().unwrap(), - "privileged".into(), - ); +/// Test user that's granted all privileges, used for automated testing and +/// local development +// TODO-security Once we have a way to bootstrap the initial Silo with the +// initial privileged user, this user should be created in the test suite, +// not automatically at Nexus startup. See omicron#2305. +pub static USER_TEST_PRIVILEGED: Lazy = Lazy::new(|| { + db::model::SiloUser::new( + *db::fixed_data::silo::SILO_ID, + // "4007" looks a bit like "root". + "001de000-05e4-4000-8000-000000004007".parse().unwrap(), + "privileged".into(), + ) +}); - /// Role assignments needed for the privileged user - pub static ref ROLE_ASSIGNMENTS_PRIVILEGED: - Vec = vec![ +/// Role assignments needed for the privileged user +pub static ROLE_ASSIGNMENTS_PRIVILEGED: Lazy> = + Lazy::new(|| { + vec![ // The "test-privileged" user gets the "admin" role on the sole // Fleet as well as the default Silo. db::model::RoleAssignment::new( @@ -34,7 +35,6 @@ lazy_static! { *db::fixed_data::FLEET_ID, role_builtin::FLEET_ADMIN.role_name, ), - db::model::RoleAssignment::new( db::model::IdentityType::SiloUser, USER_TEST_PRIVILEGED.id(), @@ -42,20 +42,22 @@ lazy_static! { *db::fixed_data::silo::SILO_ID, role_builtin::SILO_ADMIN.role_name, ), - ]; + ] + }); - /// Test user that's granted no privileges, used for automated testing - // TODO-security Once we have a way to bootstrap the initial Silo with the - // initial privileged user, this user should be created in the test suite, - // not automatically at Nexus startup. See omicron#2305. - pub static ref USER_TEST_UNPRIVILEGED: db::model::SiloUser = +/// Test user that's granted no privileges, used for automated testing +// TODO-security Once we have a way to bootstrap the initial Silo with the +// initial privileged user, this user should be created in the test suite, +// not automatically at Nexus startup. See omicron#2305. +pub static USER_TEST_UNPRIVILEGED: Lazy = + Lazy::new(|| { db::model::SiloUser::new( *db::fixed_data::silo::SILO_ID, // 60001 is the decimal uid for "nobody" on Helios. "001de000-05e4-4000-8000-000000060001".parse().unwrap(), "unprivileged".into(), - ); -} + ) + }); #[cfg(test)] mod test { diff --git a/nexus/db-queries/src/db/fixed_data/user_builtin.rs b/nexus/db-queries/src/db/fixed_data/user_builtin.rs index 87f33fa355..1e96802683 100644 --- a/nexus/db-queries/src/db/fixed_data/user_builtin.rs +++ b/nexus/db-queries/src/db/fixed_data/user_builtin.rs @@ -3,8 +3,8 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. //! Built-in users -use lazy_static::lazy_static; use omicron_common::api; +use once_cell::sync::Lazy; use uuid::Uuid; pub struct UserBuiltinConfig { @@ -27,61 +27,65 @@ impl UserBuiltinConfig { } } -lazy_static! { - /// Internal user used for seeding initial database data - // NOTE: This uuid and name are duplicated in dbinit.sql. - pub static ref USER_DB_INIT: UserBuiltinConfig = - UserBuiltinConfig::new_static( - // "0001" is the first possible user that wouldn't be confused with - // 0, or root. - "001de000-05e4-4000-8000-000000000001", - "db-init", - "used for seeding initial database data", - ); +/// Internal user used for seeding initial database data +// NOTE: This uuid and name are duplicated in dbinit.sql. +pub static USER_DB_INIT: Lazy = Lazy::new(|| { + UserBuiltinConfig::new_static( + // "0001" is the first possible user that wouldn't be confused with + // 0, or root. + "001de000-05e4-4000-8000-000000000001", + "db-init", + "used for seeding initial database data", + ) +}); - /// Internal user for performing operations to manage the - /// provisioning of services across the fleet. - pub static ref USER_SERVICE_BALANCER: UserBuiltinConfig = - UserBuiltinConfig::new_static( - "001de000-05e4-4000-8000-00000000bac3", - "service-balancer", - "used for Nexus-driven service balancing", - ); +/// Internal user for performing operations to manage the +/// provisioning of services across the fleet. +pub static USER_SERVICE_BALANCER: Lazy = Lazy::new(|| { + UserBuiltinConfig::new_static( + "001de000-05e4-4000-8000-00000000bac3", + "service-balancer", + "used for Nexus-driven service balancing", + ) +}); - /// Internal user used by Nexus when handling internal API requests - pub static ref USER_INTERNAL_API: UserBuiltinConfig = - UserBuiltinConfig::new_static( - "001de000-05e4-4000-8000-000000000002", - "internal-api", - "used by Nexus when handling internal API requests", - ); +/// Internal user used by Nexus when handling internal API requests +pub static USER_INTERNAL_API: Lazy = Lazy::new(|| { + UserBuiltinConfig::new_static( + "001de000-05e4-4000-8000-000000000002", + "internal-api", + "used by Nexus when handling internal API requests", + ) +}); - /// Internal user used by Nexus to read privileged control plane data - pub static ref USER_INTERNAL_READ: UserBuiltinConfig = - UserBuiltinConfig::new_static( - // "4ead" looks like "read" - "001de000-05e4-4000-8000-000000004ead", - "internal-read", - "used by Nexus to read privileged control plane data", - ); +/// Internal user used by Nexus to read privileged control plane data +pub static USER_INTERNAL_READ: Lazy = Lazy::new(|| { + UserBuiltinConfig::new_static( + // "4ead" looks like "read" + "001de000-05e4-4000-8000-000000004ead", + "internal-read", + "used by Nexus to read privileged control plane data", + ) +}); - /// Internal user used by Nexus when recovering sagas - pub static ref USER_SAGA_RECOVERY: UserBuiltinConfig = - UserBuiltinConfig::new_static( - // "3a8a" looks a bit like "saga". - "001de000-05e4-4000-8000-000000003a8a", - "saga-recovery", - "used by Nexus when recovering sagas", - ); +/// Internal user used by Nexus when recovering sagas +pub static USER_SAGA_RECOVERY: Lazy = Lazy::new(|| { + UserBuiltinConfig::new_static( + // "3a8a" looks a bit like "saga". + "001de000-05e4-4000-8000-000000003a8a", + "saga-recovery", + "used by Nexus when recovering sagas", + ) +}); - /// Internal user used by Nexus when authenticating external requests - pub static ref USER_EXTERNAL_AUTHN: UserBuiltinConfig = - UserBuiltinConfig::new_static( - "001de000-05e4-4000-8000-000000000003", - "external-authn", - "used by Nexus when authenticating external requests", - ); -} +/// Internal user used by Nexus when authenticating external requests +pub static USER_EXTERNAL_AUTHN: Lazy = Lazy::new(|| { + UserBuiltinConfig::new_static( + "001de000-05e4-4000-8000-000000000003", + "external-authn", + "used by Nexus when authenticating external requests", + ) +}); #[cfg(test)] mod test { diff --git a/nexus/db-queries/src/db/fixed_data/vpc.rs b/nexus/db-queries/src/db/fixed_data/vpc.rs index 6571e5c5f9..c71b655ddc 100644 --- a/nexus/db-queries/src/db/fixed_data/vpc.rs +++ b/nexus/db-queries/src/db/fixed_data/vpc.rs @@ -4,31 +4,35 @@ use crate::db; use crate::db::datastore::SERVICES_DB_NAME; -use lazy_static::lazy_static; use nexus_types::external_api::params; use omicron_common::address::SERVICE_VPC_IPV6_PREFIX; use omicron_common::api::external::IdentityMetadataCreateParams; +use once_cell::sync::Lazy; -lazy_static! { - /// UUID of built-in VPC for internal services on the rack. - pub static ref SERVICES_VPC_ID: uuid::Uuid = "001de000-074c-4000-8000-000000000000" +/// UUID of built-in VPC for internal services on the rack. +pub static SERVICES_VPC_ID: Lazy = Lazy::new(|| { + "001de000-074c-4000-8000-000000000000" .parse() - .expect("invalid uuid for builtin services vpc id"); + .expect("invalid uuid for builtin services vpc id") +}); - /// UUID of VpcRouter for built-in Services VPC. - pub static ref SERVICES_VPC_ROUTER_ID: uuid::Uuid = - "001de000-074c-4000-8000-000000000001" - .parse() - .expect("invalid uuid for builtin services vpc router id"); +/// UUID of VpcRouter for built-in Services VPC. +pub static SERVICES_VPC_ROUTER_ID: Lazy = Lazy::new(|| { + "001de000-074c-4000-8000-000000000001" + .parse() + .expect("invalid uuid for builtin services vpc router id") +}); - /// UUID of default route for built-in Services VPC. - pub static ref SERVICES_VPC_DEFAULT_ROUTE_ID: uuid::Uuid = - "001de000-074c-4000-8000-000000000002" - .parse() - .expect("invalid uuid for builtin services vpc default route id"); +/// UUID of default route for built-in Services VPC. +pub static SERVICES_VPC_DEFAULT_ROUTE_ID: Lazy = Lazy::new(|| { + "001de000-074c-4000-8000-000000000002" + .parse() + .expect("invalid uuid for builtin services vpc default route id") +}); - /// Built-in VPC for internal services on the rack. - pub static ref SERVICES_VPC: db::model::IncompleteVpc = db::model::IncompleteVpc::new( +/// Built-in VPC for internal services on the rack. +pub static SERVICES_VPC: Lazy = Lazy::new(|| { + db::model::IncompleteVpc::new( *SERVICES_VPC_ID, *super::project::SERVICES_PROJECT_ID, *SERVICES_VPC_ROUTER_ID, @@ -43,5 +47,5 @@ lazy_static! { ) // `IncompleteVpc::new` only fails if given an invalid `ipv6_prefix` // but we know `SERVICE_VPC_IPV6_PREFIX` is valid. - .unwrap(); -} + .unwrap() +}); diff --git a/nexus/db-queries/src/db/fixed_data/vpc_firewall_rule.rs b/nexus/db-queries/src/db/fixed_data/vpc_firewall_rule.rs index 3fae24abee..5062b1a11c 100644 --- a/nexus/db-queries/src/db/fixed_data/vpc_firewall_rule.rs +++ b/nexus/db-queries/src/db/fixed_data/vpc_firewall_rule.rs @@ -2,72 +2,63 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use lazy_static::lazy_static; use nexus_types::identity::Resource; use omicron_common::api::external::{ L4PortRange, VpcFirewallRuleAction, VpcFirewallRuleDirection, VpcFirewallRuleFilter, VpcFirewallRulePriority, VpcFirewallRuleProtocol, VpcFirewallRuleStatus, VpcFirewallRuleTarget, VpcFirewallRuleUpdate, }; +use once_cell::sync::Lazy; -lazy_static! { - /// Built-in VPC firewall rule for External DNS. - pub static ref DNS_VPC_FW_RULE: VpcFirewallRuleUpdate = VpcFirewallRuleUpdate { +/// Built-in VPC firewall rule for External DNS. +pub static DNS_VPC_FW_RULE: Lazy = + Lazy::new(|| VpcFirewallRuleUpdate { name: "external-dns-inbound".parse().unwrap(), description: "allow inbound connections for DNS from anywhere" .to_string(), status: VpcFirewallRuleStatus::Enabled, direction: VpcFirewallRuleDirection::Inbound, - targets: vec![ - VpcFirewallRuleTarget::Subnet( - super::vpc_subnet::DNS_VPC_SUBNET.name().clone(), - ), - ], + targets: vec![VpcFirewallRuleTarget::Subnet( + super::vpc_subnet::DNS_VPC_SUBNET.name().clone(), + )], filters: VpcFirewallRuleFilter { hosts: None, protocols: Some(vec![VpcFirewallRuleProtocol::Udp]), - ports: Some( - vec![ - L4PortRange { - first: 53.try_into().unwrap(), - last: 53.try_into().unwrap(), - }, - ], - ), + ports: Some(vec![L4PortRange { + first: 53.try_into().unwrap(), + last: 53.try_into().unwrap(), + }]), }, action: VpcFirewallRuleAction::Allow, priority: VpcFirewallRulePriority(65534), - }; + }); - /// Built-in VPC firewall rule for Nexus. - pub static ref NEXUS_VPC_FW_RULE: VpcFirewallRuleUpdate = VpcFirewallRuleUpdate { +/// Built-in VPC firewall rule for Nexus. +pub static NEXUS_VPC_FW_RULE: Lazy = + Lazy::new(|| VpcFirewallRuleUpdate { name: "nexus-inbound".parse().unwrap(), - description: "allow inbound connections for console & api from anywhere" - .to_string(), + description: + "allow inbound connections for console & api from anywhere" + .to_string(), status: VpcFirewallRuleStatus::Enabled, direction: VpcFirewallRuleDirection::Inbound, - targets: vec![ - VpcFirewallRuleTarget::Subnet( - super::vpc_subnet::NEXUS_VPC_SUBNET.name().clone(), - ), - ], + targets: vec![VpcFirewallRuleTarget::Subnet( + super::vpc_subnet::NEXUS_VPC_SUBNET.name().clone(), + )], filters: VpcFirewallRuleFilter { hosts: None, protocols: Some(vec![VpcFirewallRuleProtocol::Tcp]), - ports: Some( - vec![ - L4PortRange { - first: 80.try_into().unwrap(), - last: 80.try_into().unwrap(), - }, - L4PortRange { - first: 443.try_into().unwrap(), - last: 443.try_into().unwrap(), - }, - ], - ), + ports: Some(vec![ + L4PortRange { + first: 80.try_into().unwrap(), + last: 80.try_into().unwrap(), + }, + L4PortRange { + first: 443.try_into().unwrap(), + last: 443.try_into().unwrap(), + }, + ]), }, action: VpcFirewallRuleAction::Allow, priority: VpcFirewallRulePriority(65534), - }; -} + }); diff --git a/nexus/db-queries/src/db/fixed_data/vpc_subnet.rs b/nexus/db-queries/src/db/fixed_data/vpc_subnet.rs index 59bc87b34c..c42d4121c9 100644 --- a/nexus/db-queries/src/db/fixed_data/vpc_subnet.rs +++ b/nexus/db-queries/src/db/fixed_data/vpc_subnet.rs @@ -3,32 +3,37 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. use crate::db::model::VpcSubnet; -use lazy_static::lazy_static; use omicron_common::address::{ DNS_OPTE_IPV4_SUBNET, DNS_OPTE_IPV6_SUBNET, NEXUS_OPTE_IPV4_SUBNET, NEXUS_OPTE_IPV6_SUBNET, NTP_OPTE_IPV4_SUBNET, NTP_OPTE_IPV6_SUBNET, }; use omicron_common::api::external::IdentityMetadataCreateParams; +use once_cell::sync::Lazy; -lazy_static! { - /// UUID of built-in VPC Subnet for External DNS. - pub static ref DNS_VPC_SUBNET_ID: uuid::Uuid = "001de000-c470-4000-8000-000000000001" +/// UUID of built-in VPC Subnet for External DNS. +pub static DNS_VPC_SUBNET_ID: Lazy = Lazy::new(|| { + "001de000-c470-4000-8000-000000000001" .parse() - .expect("invalid uuid for builtin external dns vpc subnet id"); + .expect("invalid uuid for builtin external dns vpc subnet id") +}); - /// UUID of built-in VPC Subnet for Nexus. - pub static ref NEXUS_VPC_SUBNET_ID: uuid::Uuid = "001de000-c470-4000-8000-000000000002" +/// UUID of built-in VPC Subnet for Nexus. +pub static NEXUS_VPC_SUBNET_ID: Lazy = Lazy::new(|| { + "001de000-c470-4000-8000-000000000002" .parse() - .expect("invalid uuid for builtin nexus vpc subnet id"); + .expect("invalid uuid for builtin nexus vpc subnet id") +}); - /// UUID of built-in VPC Subnet for Boundary NTP. - pub static ref NTP_VPC_SUBNET_ID: uuid::Uuid = "001de000-c470-4000-8000-000000000003" +/// UUID of built-in VPC Subnet for Boundary NTP. +pub static NTP_VPC_SUBNET_ID: Lazy = Lazy::new(|| { + "001de000-c470-4000-8000-000000000003" .parse() - .expect("invalid uuid for builtin boundary ntp vpc subnet id"); + .expect("invalid uuid for builtin boundary ntp vpc subnet id") +}); - - /// Built-in VPC Subnet for External DNS. - pub static ref DNS_VPC_SUBNET: VpcSubnet = VpcSubnet::new( +/// Built-in VPC Subnet for External DNS. +pub static DNS_VPC_SUBNET: Lazy = Lazy::new(|| { + VpcSubnet::new( *DNS_VPC_SUBNET_ID, *super::vpc::SERVICES_VPC_ID, IdentityMetadataCreateParams { @@ -38,10 +43,12 @@ lazy_static! { }, *DNS_OPTE_IPV4_SUBNET, *DNS_OPTE_IPV6_SUBNET, - ); + ) +}); - /// Built-in VPC Subnet for Nexus. - pub static ref NEXUS_VPC_SUBNET: VpcSubnet = VpcSubnet::new( +/// Built-in VPC Subnet for Nexus. +pub static NEXUS_VPC_SUBNET: Lazy = Lazy::new(|| { + VpcSubnet::new( *NEXUS_VPC_SUBNET_ID, *super::vpc::SERVICES_VPC_ID, IdentityMetadataCreateParams { @@ -51,10 +58,12 @@ lazy_static! { }, *NEXUS_OPTE_IPV4_SUBNET, *NEXUS_OPTE_IPV6_SUBNET, - ); + ) +}); - /// Built-in VPC Subnet for Boundary NTP. - pub static ref NTP_VPC_SUBNET: VpcSubnet = VpcSubnet::new( +/// Built-in VPC Subnet for Boundary NTP. +pub static NTP_VPC_SUBNET: Lazy = Lazy::new(|| { + VpcSubnet::new( *NTP_VPC_SUBNET_ID, *super::vpc::SERVICES_VPC_ID, IdentityMetadataCreateParams { @@ -64,5 +73,5 @@ lazy_static! { }, *NTP_OPTE_IPV4_SUBNET, *NTP_OPTE_IPV6_SUBNET, - ); -} + ) +}); diff --git a/nexus/db-queries/src/db/queries/network_interface.rs b/nexus/db-queries/src/db/queries/network_interface.rs index 1dbe57da6f..6d00b4bc29 100644 --- a/nexus/db-queries/src/db/queries/network_interface.rs +++ b/nexus/db-queries/src/db/queries/network_interface.rs @@ -30,6 +30,7 @@ use nexus_db_model::NetworkInterfaceKindEnum; use omicron_common::api::external; use omicron_common::api::external::MacAddr; use omicron_common::nexus_config::NUM_INITIAL_RESERVED_IP_ADDRESSES; +use once_cell::sync::Lazy; use std::net::IpAddr; use uuid::Uuid; @@ -42,36 +43,35 @@ pub(crate) const MAX_NICS: usize = 8; // These are sentinel values and other constants used to verify the state of the // system when operating on network interfaces -lazy_static::lazy_static! { - // States an instance must be in to operate on its network interfaces, in - // most situations. - static ref INSTANCE_STOPPED: db::model::InstanceState = - db::model::InstanceState(external::InstanceState::Stopped); - - static ref INSTANCE_FAILED: db::model::InstanceState = - db::model::InstanceState(external::InstanceState::Failed); - - // An instance can be in the creating state while we manipulate its - // interfaces. The intention is for this only to be the case during sagas. - static ref INSTANCE_CREATING: db::model::InstanceState = - db::model::InstanceState(external::InstanceState::Creating); - - // A sentinel value for the instance state when the instance actually does - // not exist. - static ref INSTANCE_DESTROYED: db::model::InstanceState = - db::model::InstanceState(external::InstanceState::Destroyed); - - // A sentinel value for the instance state when the instance has an active - // VMM, irrespective of that VMM's actual state. - static ref INSTANCE_RUNNING: db::model::InstanceState = - db::model::InstanceState(external::InstanceState::Running); - - static ref NO_INSTANCE_SENTINEL_STRING: String = - String::from(NO_INSTANCE_SENTINEL); - - static ref INSTANCE_BAD_STATE_SENTINEL_STRING: String = - String::from(INSTANCE_BAD_STATE_SENTINEL); -} + +// States an instance must be in to operate on its network interfaces, in +// most situations. +static INSTANCE_STOPPED: Lazy = + Lazy::new(|| db::model::InstanceState(external::InstanceState::Stopped)); + +static INSTANCE_FAILED: Lazy = + Lazy::new(|| db::model::InstanceState(external::InstanceState::Failed)); + +// An instance can be in the creating state while we manipulate its +// interfaces. The intention is for this only to be the case during sagas. +static INSTANCE_CREATING: Lazy = + Lazy::new(|| db::model::InstanceState(external::InstanceState::Creating)); + +// A sentinel value for the instance state when the instance actually does +// not exist. +static INSTANCE_DESTROYED: Lazy = + Lazy::new(|| db::model::InstanceState(external::InstanceState::Destroyed)); + +// A sentinel value for the instance state when the instance has an active +// VMM, irrespective of that VMM's actual state. +static INSTANCE_RUNNING: Lazy = + Lazy::new(|| db::model::InstanceState(external::InstanceState::Running)); + +static NO_INSTANCE_SENTINEL_STRING: Lazy = + Lazy::new(|| String::from(NO_INSTANCE_SENTINEL)); + +static INSTANCE_BAD_STATE_SENTINEL_STRING: Lazy = + Lazy::new(|| String::from(INSTANCE_BAD_STATE_SENTINEL)); // Uncastable sentinel used to detect when an instance exists, but is not // in the right state to have its network interfaces altered diff --git a/nexus/db-queries/src/db/saga_recovery.rs b/nexus/db-queries/src/db/saga_recovery.rs index 802093b889..55cda03c3c 100644 --- a/nexus/db-queries/src/db/saga_recovery.rs +++ b/nexus/db-queries/src/db/saga_recovery.rs @@ -305,9 +305,9 @@ mod test { use super::*; use crate::context::OpContext; use crate::db::test_utils::UnpluggableCockroachDbSecStore; - use lazy_static::lazy_static; use nexus_test_utils::db::test_setup_database; use omicron_test_utils::dev; + use once_cell::sync::Lazy; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use steno::{ new_action_noop_undo, Action, ActionContext, ActionError, @@ -376,12 +376,10 @@ mod test { type ExecContextType = TestContext; } - lazy_static! { - static ref ACTION_N1: Arc> = - new_action_noop_undo("n1_action", node_one); - static ref ACTION_N2: Arc> = - new_action_noop_undo("n2_action", node_two); - } + static ACTION_N1: Lazy>> = + Lazy::new(|| new_action_noop_undo("n1_action", node_one)); + static ACTION_N2: Lazy>> = + Lazy::new(|| new_action_noop_undo("n2_action", node_two)); fn registry_create() -> Arc> { let mut registry = ActionRegistry::new(); diff --git a/nexus/defaults/Cargo.toml b/nexus/defaults/Cargo.toml index 0724b5bf4d..535b78054b 100644 --- a/nexus/defaults/Cargo.toml +++ b/nexus/defaults/Cargo.toml @@ -6,7 +6,7 @@ license = "MPL-2.0" [dependencies] ipnetwork.workspace = true -lazy_static.workspace = true +once_cell.workspace = true rand.workspace = true serde_json.workspace = true diff --git a/nexus/defaults/src/lib.rs b/nexus/defaults/src/lib.rs index be1ce2193c..dd08b4e4ab 100644 --- a/nexus/defaults/src/lib.rs +++ b/nexus/defaults/src/lib.rs @@ -6,10 +6,10 @@ use ipnetwork::Ipv4Network; use ipnetwork::Ipv6Network; -use lazy_static::lazy_static; use omicron_common::api::external; use omicron_common::api::external::Ipv4Net; use omicron_common::api::external::Ipv6Net; +use once_cell::sync::Lazy; use std::net::Ipv4Addr; use std::net::Ipv6Addr; @@ -17,51 +17,51 @@ use std::net::Ipv6Addr; /// instance. pub const DEFAULT_PRIMARY_NIC_NAME: &str = "net0"; -lazy_static! { - /// The default IPv4 subnet range assigned to the default VPC Subnet, when - /// the VPC is created, if one is not provided in the request. See - /// for details. - pub static ref DEFAULT_VPC_SUBNET_IPV4_BLOCK: external::Ipv4Net = - Ipv4Net(Ipv4Network::new(Ipv4Addr::new(172, 30, 0, 0), 22).unwrap()); -} +/// The default IPv4 subnet range assigned to the default VPC Subnet, when +/// the VPC is created, if one is not provided in the request. See +/// for details. +pub static DEFAULT_VPC_SUBNET_IPV4_BLOCK: Lazy = + Lazy::new(|| { + Ipv4Net(Ipv4Network::new(Ipv4Addr::new(172, 30, 0, 0), 22).unwrap()) + }); -lazy_static! { - pub static ref DEFAULT_FIREWALL_RULES: external::VpcFirewallRuleUpdateParams = +pub static DEFAULT_FIREWALL_RULES: Lazy = + Lazy::new(|| { serde_json::from_str(r#"{ - "rules": [ - { - "name": "allow-internal-inbound", - "status": "enabled", - "direction": "inbound", - "targets": [ { "type": "vpc", "value": "default" } ], - "filters": { "hosts": [ { "type": "vpc", "value": "default" } ] }, - "action": "allow", - "priority": 65534, - "description": "allow inbound traffic to all instances within the VPC if originated within the VPC" - }, - { - "name": "allow-ssh", - "status": "enabled", - "direction": "inbound", - "targets": [ { "type": "vpc", "value": "default" } ], - "filters": { "ports": [ "22" ], "protocols": [ "TCP" ] }, - "action": "allow", - "priority": 65534, - "description": "allow inbound TCP connections on port 22 from anywhere" - }, - { - "name": "allow-icmp", - "status": "enabled", - "direction": "inbound", - "targets": [ { "type": "vpc", "value": "default" } ], - "filters": { "protocols": [ "ICMP" ] }, - "action": "allow", - "priority": 65534, - "description": "allow inbound ICMP traffic from anywhere" - } - ] - }"#).unwrap(); -} + "rules": [ + { + "name": "allow-internal-inbound", + "status": "enabled", + "direction": "inbound", + "targets": [ { "type": "vpc", "value": "default" } ], + "filters": { "hosts": [ { "type": "vpc", "value": "default" } ] }, + "action": "allow", + "priority": 65534, + "description": "allow inbound traffic to all instances within the VPC if originated within the VPC" + }, + { + "name": "allow-ssh", + "status": "enabled", + "direction": "inbound", + "targets": [ { "type": "vpc", "value": "default" } ], + "filters": { "ports": [ "22" ], "protocols": [ "TCP" ] }, + "action": "allow", + "priority": 65534, + "description": "allow inbound TCP connections on port 22 from anywhere" + }, + { + "name": "allow-icmp", + "status": "enabled", + "direction": "inbound", + "targets": [ { "type": "vpc", "value": "default" } ], + "filters": { "protocols": [ "ICMP" ] }, + "action": "allow", + "priority": 65534, + "description": "allow inbound ICMP traffic from anywhere" + } + ] + }"#).unwrap() + }); /// Generate a random VPC IPv6 prefix, in the range `fd00::/48`. pub fn random_vpc_ipv6_prefix() -> Result { diff --git a/nexus/preprocessed_configs/config.xml b/nexus/preprocessed_configs/config.xml new file mode 100644 index 0000000000..9b13f12aea --- /dev/null +++ b/nexus/preprocessed_configs/config.xml @@ -0,0 +1,41 @@ + + + + + trace + true + + + 8123 + 9000 + 9004 + + ./ + + true + + + + + + + ::/0 + + + default + default + 1 + + + + + + + + + + + \ No newline at end of file diff --git a/nexus/src/external_api/console_api.rs b/nexus/src/external_api/console_api.rs index d779d34459..90450c3145 100644 --- a/nexus/src/external_api/console_api.rs +++ b/nexus/src/external_api/console_api.rs @@ -17,7 +17,6 @@ use dropshot::{ }; use http::{header, Response, StatusCode, Uri}; use hyper::Body; -use lazy_static::lazy_static; use mime_guess; use nexus_db_model::AuthenticationMode; use nexus_db_queries::authn::silos::IdentityProviderType; @@ -36,6 +35,7 @@ use nexus_types::external_api::params; use nexus_types::identity::Resource; use omicron_common::api::external::http_pagination::PaginatedBy; use omicron_common::api::external::{DataPageParams, Error, NameOrId}; +use once_cell::sync::Lazy; use parse_display::Display; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -810,15 +810,15 @@ fn not_found(internal_msg: &str) -> HttpError { HttpError::for_not_found(None, internal_msg.to_string()) } -lazy_static! { - static ref ALLOWED_EXTENSIONS: HashSet = HashSet::from( +static ALLOWED_EXTENSIONS: Lazy> = Lazy::new(|| { + HashSet::from( [ "js", "css", "html", "ico", "map", "otf", "png", "svg", "ttf", "txt", "webp", "woff", "woff2", ] - .map(|s| OsString::from(s)) - ); -} + .map(|s| OsString::from(s)), + ) +}); /// Starting from `root_dir`, follow the segments of `path` down the file tree /// until we find a file (or not). Do not follow symlinks. diff --git a/nexus/tests/integration_tests/endpoints.rs b/nexus/tests/integration_tests/endpoints.rs index bd6df210c0..694f3cd4ee 100644 --- a/nexus/tests/integration_tests/endpoints.rs +++ b/nexus/tests/integration_tests/endpoints.rs @@ -11,7 +11,6 @@ use crate::integration_tests::unauthorized::HTTP_SERVER; use chrono::Utc; use http::method::Method; use internal_dns::names::DNS_ZONE_EXTERNAL_TESTING; -use lazy_static::lazy_static; use nexus_db_queries::authn; use nexus_db_queries::db::fixed_data::silo::DEFAULT_SILO; use nexus_db_queries::db::identity::Resource; @@ -38,219 +37,261 @@ use omicron_common::api::external::RouteTarget; use omicron_common::api::external::SemverVersion; use omicron_common::api::external::VpcFirewallRuleUpdateParams; use omicron_test_utils::certificates::CertificateChain; +use once_cell::sync::Lazy; use std::net::IpAddr; use std::net::Ipv4Addr; use std::str::FromStr; use uuid::Uuid; -lazy_static! { - pub static ref HARDWARE_RACK_URL: String = - format!("/v1/system/hardware/racks/{}", RACK_UUID); - pub static ref HARDWARE_UNINITIALIZED_SLEDS: String = - format!("/v1/system/hardware/uninitialized-sleds"); - pub static ref HARDWARE_SLED_URL: String = - format!("/v1/system/hardware/sleds/{}", SLED_AGENT_UUID); - pub static ref HARDWARE_SLED_PROVISION_STATE_URL: String = - format!("/v1/system/hardware/sleds/{}/provision-state", SLED_AGENT_UUID); - pub static ref DEMO_SLED_PROVISION_STATE: params::SledProvisionStateParams = +pub static HARDWARE_RACK_URL: Lazy = + Lazy::new(|| format!("/v1/system/hardware/racks/{}", RACK_UUID)); +pub const HARDWARE_UNINITIALIZED_SLEDS: &'static str = + "/v1/system/hardware/uninitialized-sleds"; +pub static HARDWARE_SLED_URL: Lazy = + Lazy::new(|| format!("/v1/system/hardware/sleds/{}", SLED_AGENT_UUID)); +pub static HARDWARE_SLED_PROVISION_STATE_URL: Lazy = Lazy::new(|| { + format!("/v1/system/hardware/sleds/{}/provision-state", SLED_AGENT_UUID) +}); +pub static DEMO_SLED_PROVISION_STATE: Lazy = + Lazy::new(|| { params::SledProvisionStateParams { state: nexus_types::external_api::views::SledProvisionState::NonProvisionable, - }; - pub static ref HARDWARE_SWITCH_URL: String = - format!("/v1/system/hardware/switches/{}", SWITCH_UUID); - pub static ref HARDWARE_DISK_URL: String = - format!("/v1/system/hardware/disks"); - pub static ref HARDWARE_SLED_DISK_URL: String = - format!("/v1/system/hardware/sleds/{}/disks", SLED_AGENT_UUID); - - pub static ref SLED_INSTANCES_URL: String = - format!("/v1/system/hardware/sleds/{}/instances", SLED_AGENT_UUID); - - pub static ref DEMO_UNINITIALIZED_SLED: UninitializedSled = UninitializedSled { + } + }); + +pub static HARDWARE_SWITCH_URL: Lazy = + Lazy::new(|| format!("/v1/system/hardware/switches/{}", SWITCH_UUID)); +pub const HARDWARE_DISK_URL: &'static str = "/v1/system/hardware/disks"; +pub static HARDWARE_SLED_DISK_URL: Lazy = Lazy::new(|| { + format!("/v1/system/hardware/sleds/{}/disks", SLED_AGENT_UUID) +}); + +pub static SLED_INSTANCES_URL: Lazy = Lazy::new(|| { + format!("/v1/system/hardware/sleds/{}/instances", SLED_AGENT_UUID) +}); +pub static DEMO_UNINITIALIZED_SLED: Lazy = + Lazy::new(|| UninitializedSled { baseboard: Baseboard { serial: "demo-serial".to_string(), part: "demo-part".to_string(), - revision: 6 + revision: 6, }, rack_id: Uuid::new_v4(), - cubby: 1 - }; - - // Global policy - pub static ref SYSTEM_POLICY_URL: &'static str = "/v1/system/policy"; - - // Silo used for testing - pub static ref DEMO_SILO_NAME: Name = "demo-silo".parse().unwrap(); - pub static ref DEMO_SILO_URL: String = - format!("/v1/system/silos/{}", *DEMO_SILO_NAME); - pub static ref DEMO_SILO_POLICY_URL: String = - format!("/v1/system/silos/{}/policy", *DEMO_SILO_NAME); - pub static ref DEMO_SILO_QUOTAS_URL: String = - format!("/v1/system/silos/{}/quotas", *DEMO_SILO_NAME); - pub static ref DEMO_SILO_CREATE: params::SiloCreate = - params::SiloCreate { - identity: IdentityMetadataCreateParams { - name: DEMO_SILO_NAME.clone(), - description: String::from(""), - }, - quotas: params::SiloQuotasCreate::arbitrarily_high_default(), - discoverable: true, - identity_mode: shared::SiloIdentityMode::SamlJit, - admin_group_name: None, - tls_certificates: vec![], - mapped_fleet_roles: Default::default(), - }; - // Use the default Silo for testing the local IdP - pub static ref DEMO_SILO_USERS_CREATE_URL: String = format!( + cubby: 1, + }); + +// Global policy +pub const SYSTEM_POLICY_URL: &'static str = "/v1/system/policy"; + +// Silo used for testing +pub static DEMO_SILO_NAME: Lazy = + Lazy::new(|| "demo-silo".parse().unwrap()); +pub static DEMO_SILO_URL: Lazy = + Lazy::new(|| format!("/v1/system/silos/{}", *DEMO_SILO_NAME)); +pub static DEMO_SILO_POLICY_URL: Lazy = + Lazy::new(|| format!("/v1/system/silos/{}/policy", *DEMO_SILO_NAME)); +pub static DEMO_SILO_QUOTAS_URL: Lazy = + Lazy::new(|| format!("/v1/system/silos/{}/quotas", *DEMO_SILO_NAME)); +pub static DEMO_SILO_CREATE: Lazy = + Lazy::new(|| params::SiloCreate { + identity: IdentityMetadataCreateParams { + name: DEMO_SILO_NAME.clone(), + description: String::from(""), + }, + quotas: params::SiloQuotasCreate::arbitrarily_high_default(), + discoverable: true, + identity_mode: shared::SiloIdentityMode::SamlJit, + admin_group_name: None, + tls_certificates: vec![], + mapped_fleet_roles: Default::default(), + }); + +// Use the default Silo for testing the local IdP +pub static DEMO_SILO_USERS_CREATE_URL: Lazy = Lazy::new(|| { + format!( "/v1/system/identity-providers/local/users?silo={}", DEFAULT_SILO.identity().name, - ); - pub static ref DEMO_SILO_USERS_LIST_URL: String = format!( - "/v1/system/users?silo={}", - DEFAULT_SILO.identity().name, - ); - pub static ref DEMO_SILO_USER_ID_GET_URL: String = format!( - "/v1/system/users/{{id}}?silo={}", - DEFAULT_SILO.identity().name, - ); - pub static ref DEMO_SILO_USER_ID_DELETE_URL: String = format!( + ) +}); +pub static DEMO_SILO_USERS_LIST_URL: Lazy = Lazy::new(|| { + format!("/v1/system/users?silo={}", DEFAULT_SILO.identity().name,) +}); +pub static DEMO_SILO_USER_ID_GET_URL: Lazy = Lazy::new(|| { + format!("/v1/system/users/{{id}}?silo={}", DEFAULT_SILO.identity().name,) +}); +pub static DEMO_SILO_USER_ID_DELETE_URL: Lazy = Lazy::new(|| { + format!( "/v1/system/identity-providers/local/users/{{id}}?silo={}", DEFAULT_SILO.identity().name, - ); - pub static ref DEMO_SILO_USER_ID_SET_PASSWORD_URL: String = format!( + ) +}); +pub static DEMO_SILO_USER_ID_SET_PASSWORD_URL: Lazy = Lazy::new(|| { + format!( "/v1/system/identity-providers/local/users/{{id}}/set-password?silo={}", DEFAULT_SILO.identity().name, - ); - - // Project used for testing - pub static ref DEMO_PROJECT_NAME: Name = "demo-project".parse().unwrap(); - pub static ref DEMO_PROJECT_URL: String = - format!("/v1/projects/{}", *DEMO_PROJECT_NAME); - pub static ref DEMO_PROJECT_SELECTOR: String = - format!("project={}", *DEMO_PROJECT_NAME); - pub static ref DEMO_PROJECT_POLICY_URL: String = - format!("/v1/projects/{}/policy", *DEMO_PROJECT_NAME); - pub static ref DEMO_PROJECT_URL_DISKS: String = - format!("/v1/disks?project={}", *DEMO_PROJECT_NAME); - pub static ref DEMO_PROJECT_URL_IMAGES: String = - format!("/v1/images?project={}", *DEMO_PROJECT_NAME); - pub static ref DEMO_PROJECT_URL_INSTANCES: String = format!("/v1/instances?project={}", *DEMO_PROJECT_NAME); - pub static ref DEMO_PROJECT_URL_SNAPSHOTS: String = format!("/v1/snapshots?project={}", *DEMO_PROJECT_NAME); - pub static ref DEMO_PROJECT_URL_VPCS: String = format!("/v1/vpcs?project={}", *DEMO_PROJECT_NAME); - pub static ref DEMO_PROJECT_URL_FIPS: String = format!("/v1/floating-ips?project={}", *DEMO_PROJECT_NAME); - pub static ref DEMO_PROJECT_CREATE: params::ProjectCreate = - params::ProjectCreate { - identity: IdentityMetadataCreateParams { - name: DEMO_PROJECT_NAME.clone(), - description: String::from(""), - }, - }; - - // VPC used for testing - pub static ref DEMO_VPC_NAME: Name = "demo-vpc".parse().unwrap(); - pub static ref DEMO_VPC_URL: String = - format!("/v1/vpcs/{}?{}", *DEMO_VPC_NAME, *DEMO_PROJECT_SELECTOR); - - pub static ref DEMO_VPC_SELECTOR: String = - format!("project={}&vpc={}", *DEMO_PROJECT_NAME, *DEMO_VPC_NAME); - pub static ref DEMO_VPC_URL_FIREWALL_RULES: String = - format!("/v1/vpc-firewall-rules?{}", *DEMO_VPC_SELECTOR); - pub static ref DEMO_VPC_URL_ROUTERS: String = - format!("/v1/vpc-routers?{}", *DEMO_VPC_SELECTOR); - pub static ref DEMO_VPC_URL_SUBNETS: String = - format!("/v1/vpc-subnets?{}", *DEMO_VPC_SELECTOR); - pub static ref DEMO_VPC_CREATE: params::VpcCreate = - params::VpcCreate { - identity: IdentityMetadataCreateParams { - name: DEMO_VPC_NAME.clone(), - description: String::from(""), - }, - ipv6_prefix: None, - dns_name: DEMO_VPC_NAME.clone(), - }; - - // VPC Subnet used for testing - pub static ref DEMO_VPC_SUBNET_NAME: Name = - "demo-vpc-subnet".parse().unwrap(); - pub static ref DEMO_VPC_SUBNET_URL: String = - format!("/v1/vpc-subnets/{}?{}", *DEMO_VPC_SUBNET_NAME, *DEMO_VPC_SELECTOR); - pub static ref DEMO_VPC_SUBNET_INTERFACES_URL: String = - format!("/v1/vpc-subnets/{}/network-interfaces?{}", *DEMO_VPC_SUBNET_NAME, *DEMO_VPC_SELECTOR); - pub static ref DEMO_VPC_SUBNET_CREATE: params::VpcSubnetCreate = - params::VpcSubnetCreate { - identity: IdentityMetadataCreateParams { - name: DEMO_VPC_SUBNET_NAME.clone(), - description: String::from(""), - }, - ipv4_block: Ipv4Net("10.1.2.3/8".parse().unwrap()), - ipv6_block: None, - }; - - // VPC Router used for testing - pub static ref DEMO_VPC_ROUTER_NAME: Name = - "demo-vpc-router".parse().unwrap(); - pub static ref DEMO_VPC_ROUTER_URL: String = - format!("/v1/vpc-routers/{}?project={}&vpc={}", *DEMO_VPC_ROUTER_NAME, *DEMO_PROJECT_NAME, *DEMO_VPC_NAME); - pub static ref DEMO_VPC_ROUTER_URL_ROUTES: String = - format!("/v1/vpc-router-routes?project={}&vpc={}&router={}", *DEMO_PROJECT_NAME, *DEMO_VPC_NAME, *DEMO_VPC_ROUTER_NAME); - pub static ref DEMO_VPC_ROUTER_CREATE: params::VpcRouterCreate = - params::VpcRouterCreate { - identity: IdentityMetadataCreateParams { - name: DEMO_VPC_ROUTER_NAME.clone(), - description: String::from(""), - }, - }; - - // Router Route used for testing - pub static ref DEMO_ROUTER_ROUTE_NAME: Name = - "demo-router-route".parse().unwrap(); - pub static ref DEMO_ROUTER_ROUTE_URL: String = - format!("/v1/vpc-router-routes/{}?project={}&vpc={}&router={}", *DEMO_ROUTER_ROUTE_NAME, *DEMO_PROJECT_NAME, *DEMO_VPC_NAME, *DEMO_VPC_ROUTER_NAME); - pub static ref DEMO_ROUTER_ROUTE_CREATE: params::RouterRouteCreate = - params::RouterRouteCreate { - identity: IdentityMetadataCreateParams { - name: DEMO_ROUTER_ROUTE_NAME.clone(), - description: String::from(""), - }, - target: RouteTarget::Ip(IpAddr::from(Ipv4Addr::new(127, 0, 0, 1))), - destination: RouteDestination::Subnet("loopback".parse().unwrap()), - }; - - // Disk used for testing - pub static ref DEMO_DISK_NAME: Name = "demo-disk".parse().unwrap(); - // TODO: Once we can test a URL multiple times we should also a case to exercise authz for disks filtered by instances - pub static ref DEMO_DISKS_URL: String = - format!("/v1/disks?{}", *DEMO_PROJECT_SELECTOR); - pub static ref DEMO_DISK_URL: String = - format!("/v1/disks/{}?{}", *DEMO_DISK_NAME, *DEMO_PROJECT_SELECTOR); - pub static ref DEMO_DISK_CREATE: params::DiskCreate = - params::DiskCreate { - identity: IdentityMetadataCreateParams { - name: DEMO_DISK_NAME.clone(), - description: "".parse().unwrap(), - }, - disk_source: params::DiskSource::Blank { - block_size: params::BlockSize::try_from(4096).unwrap(), - }, - size: ByteCount::from_gibibytes_u32( - // divide by at least two to leave space for snapshot blocks - DiskTest::DEFAULT_ZPOOL_SIZE_GIB / 5 - ), - }; - pub static ref DEMO_DISK_METRICS_URL: String = - format!( - "/v1/disks/{}/metrics/activated?start_time={:?}&end_time={:?}&{}", - *DEMO_DISK_NAME, - Utc::now(), - Utc::now(), - *DEMO_PROJECT_SELECTOR, - ); - - // Related to importing blocks from an external source - pub static ref DEMO_IMPORT_DISK_NAME: Name = "demo-import-disk".parse().unwrap(); - pub static ref DEMO_IMPORT_DISK_URL: String = - format!("/v1/disks/{}?{}", *DEMO_IMPORT_DISK_NAME, *DEMO_PROJECT_SELECTOR); - pub static ref DEMO_IMPORT_DISK_CREATE: params::DiskCreate = + ) +}); + +// Project used for testing +pub static DEMO_PROJECT_NAME: Lazy = + Lazy::new(|| "demo-project".parse().unwrap()); +pub static DEMO_PROJECT_URL: Lazy = + Lazy::new(|| format!("/v1/projects/{}", *DEMO_PROJECT_NAME)); +pub static DEMO_PROJECT_SELECTOR: Lazy = + Lazy::new(|| format!("project={}", *DEMO_PROJECT_NAME)); +pub static DEMO_PROJECT_POLICY_URL: Lazy = + Lazy::new(|| format!("/v1/projects/{}/policy", *DEMO_PROJECT_NAME)); +pub static DEMO_PROJECT_URL_IMAGES: Lazy = + Lazy::new(|| format!("/v1/images?project={}", *DEMO_PROJECT_NAME)); +pub static DEMO_PROJECT_URL_INSTANCES: Lazy = + Lazy::new(|| format!("/v1/instances?project={}", *DEMO_PROJECT_NAME)); +pub static DEMO_PROJECT_URL_SNAPSHOTS: Lazy = + Lazy::new(|| format!("/v1/snapshots?project={}", *DEMO_PROJECT_NAME)); +pub static DEMO_PROJECT_URL_VPCS: Lazy = + Lazy::new(|| format!("/v1/vpcs?project={}", *DEMO_PROJECT_NAME)); +pub static DEMO_PROJECT_URL_FIPS: Lazy = + Lazy::new(|| format!("/v1/floating-ips?project={}", *DEMO_PROJECT_NAME)); +pub static DEMO_PROJECT_CREATE: Lazy = + Lazy::new(|| params::ProjectCreate { + identity: IdentityMetadataCreateParams { + name: DEMO_PROJECT_NAME.clone(), + description: String::from(""), + }, + }); + +// VPC used for testing +pub static DEMO_VPC_NAME: Lazy = + Lazy::new(|| "demo-vpc".parse().unwrap()); +pub static DEMO_VPC_URL: Lazy = Lazy::new(|| { + format!("/v1/vpcs/{}?{}", *DEMO_VPC_NAME, *DEMO_PROJECT_SELECTOR) +}); +pub static DEMO_VPC_SELECTOR: Lazy = Lazy::new(|| { + format!("project={}&vpc={}", *DEMO_PROJECT_NAME, *DEMO_VPC_NAME) +}); +pub static DEMO_VPC_URL_FIREWALL_RULES: Lazy = + Lazy::new(|| format!("/v1/vpc-firewall-rules?{}", *DEMO_VPC_SELECTOR)); +pub static DEMO_VPC_URL_ROUTERS: Lazy = + Lazy::new(|| format!("/v1/vpc-routers?{}", *DEMO_VPC_SELECTOR)); +pub static DEMO_VPC_URL_SUBNETS: Lazy = + Lazy::new(|| format!("/v1/vpc-subnets?{}", *DEMO_VPC_SELECTOR)); +pub static DEMO_VPC_CREATE: Lazy = + Lazy::new(|| params::VpcCreate { + identity: IdentityMetadataCreateParams { + name: DEMO_VPC_NAME.clone(), + description: String::from(""), + }, + ipv6_prefix: None, + dns_name: DEMO_VPC_NAME.clone(), + }); + +// VPC Subnet used for testing +pub static DEMO_VPC_SUBNET_NAME: Lazy = + Lazy::new(|| "demo-vpc-subnet".parse().unwrap()); +pub static DEMO_VPC_SUBNET_URL: Lazy = Lazy::new(|| { + format!("/v1/vpc-subnets/{}?{}", *DEMO_VPC_SUBNET_NAME, *DEMO_VPC_SELECTOR) +}); +pub static DEMO_VPC_SUBNET_INTERFACES_URL: Lazy = Lazy::new(|| { + format!( + "/v1/vpc-subnets/{}/network-interfaces?{}", + *DEMO_VPC_SUBNET_NAME, *DEMO_VPC_SELECTOR + ) +}); +pub static DEMO_VPC_SUBNET_CREATE: Lazy = + Lazy::new(|| params::VpcSubnetCreate { + identity: IdentityMetadataCreateParams { + name: DEMO_VPC_SUBNET_NAME.clone(), + description: String::from(""), + }, + ipv4_block: Ipv4Net("10.1.2.3/8".parse().unwrap()), + ipv6_block: None, + }); + +// VPC Router used for testing +pub static DEMO_VPC_ROUTER_NAME: Lazy = + Lazy::new(|| "demo-vpc-router".parse().unwrap()); +pub static DEMO_VPC_ROUTER_URL: Lazy = Lazy::new(|| { + format!( + "/v1/vpc-routers/{}?project={}&vpc={}", + *DEMO_VPC_ROUTER_NAME, *DEMO_PROJECT_NAME, *DEMO_VPC_NAME + ) +}); +pub static DEMO_VPC_ROUTER_URL_ROUTES: Lazy = Lazy::new(|| { + format!( + "/v1/vpc-router-routes?project={}&vpc={}&router={}", + *DEMO_PROJECT_NAME, *DEMO_VPC_NAME, *DEMO_VPC_ROUTER_NAME + ) +}); +pub static DEMO_VPC_ROUTER_CREATE: Lazy = + Lazy::new(|| params::VpcRouterCreate { + identity: IdentityMetadataCreateParams { + name: DEMO_VPC_ROUTER_NAME.clone(), + description: String::from(""), + }, + }); + +// Router Route used for testing +pub static DEMO_ROUTER_ROUTE_NAME: Lazy = + Lazy::new(|| "demo-router-route".parse().unwrap()); +pub static DEMO_ROUTER_ROUTE_URL: Lazy = Lazy::new(|| { + format!( + "/v1/vpc-router-routes/{}?project={}&vpc={}&router={}", + *DEMO_ROUTER_ROUTE_NAME, + *DEMO_PROJECT_NAME, + *DEMO_VPC_NAME, + *DEMO_VPC_ROUTER_NAME + ) +}); +pub static DEMO_ROUTER_ROUTE_CREATE: Lazy = + Lazy::new(|| params::RouterRouteCreate { + identity: IdentityMetadataCreateParams { + name: DEMO_ROUTER_ROUTE_NAME.clone(), + description: String::from(""), + }, + target: RouteTarget::Ip(IpAddr::from(Ipv4Addr::new(127, 0, 0, 1))), + destination: RouteDestination::Subnet("loopback".parse().unwrap()), + }); + +// Disk used for testing +pub static DEMO_DISK_NAME: Lazy = + Lazy::new(|| "demo-disk".parse().unwrap()); + +// TODO: Once we can test a URL multiple times we should also a case to exercise +// authz for disks filtered by instances +pub static DEMO_DISKS_URL: Lazy = + Lazy::new(|| format!("/v1/disks?{}", *DEMO_PROJECT_SELECTOR)); +pub static DEMO_DISK_URL: Lazy = Lazy::new(|| { + format!("/v1/disks/{}?{}", *DEMO_DISK_NAME, *DEMO_PROJECT_SELECTOR) +}); +pub static DEMO_DISK_CREATE: Lazy = Lazy::new(|| { + params::DiskCreate { + identity: IdentityMetadataCreateParams { + name: DEMO_DISK_NAME.clone(), + description: "".parse().unwrap(), + }, + disk_source: params::DiskSource::Blank { + block_size: params::BlockSize::try_from(4096).unwrap(), + }, + size: ByteCount::from_gibibytes_u32( + // divide by at least two to leave space for snapshot blocks + DiskTest::DEFAULT_ZPOOL_SIZE_GIB / 5, + ), + } +}); +pub static DEMO_DISK_METRICS_URL: Lazy = Lazy::new(|| { + format!( + "/v1/disks/{}/metrics/activated?start_time={:?}&end_time={:?}&{}", + *DEMO_DISK_NAME, + Utc::now(), + Utc::now(), + *DEMO_PROJECT_SELECTOR, + ) +}); + +// Related to importing blocks from an external source +pub static DEMO_IMPORT_DISK_NAME: Lazy = + Lazy::new(|| "demo-import-disk".parse().unwrap()); +pub static DEMO_IMPORT_DISK_CREATE: Lazy = + Lazy::new(|| { params::DiskCreate { identity: IdentityMetadataCreateParams { name: DEMO_IMPORT_DISK_NAME.clone(), @@ -261,381 +302,486 @@ lazy_static! { }, size: ByteCount::from_gibibytes_u32( // divide by at least two to leave space for snapshot blocks - DiskTest::DEFAULT_ZPOOL_SIZE_GIB / 5 + DiskTest::DEFAULT_ZPOOL_SIZE_GIB / 5, ), - }; - - pub static ref DEMO_IMPORT_DISK_BULK_WRITE_START_URL: String = - format!("/v1/disks/{}/bulk-write-start?{}", *DEMO_IMPORT_DISK_NAME, *DEMO_PROJECT_SELECTOR); - pub static ref DEMO_IMPORT_DISK_BULK_WRITE_URL: String = - format!("/v1/disks/{}/bulk-write?{}", *DEMO_IMPORT_DISK_NAME, *DEMO_PROJECT_SELECTOR); - pub static ref DEMO_IMPORT_DISK_BULK_WRITE_STOP_URL: String = - format!("/v1/disks/{}/bulk-write-stop?{}", *DEMO_IMPORT_DISK_NAME, *DEMO_PROJECT_SELECTOR); - pub static ref DEMO_IMPORT_DISK_FINALIZE_URL: String = - format!("/v1/disks/{}/finalize?{}", *DEMO_IMPORT_DISK_NAME, *DEMO_PROJECT_SELECTOR); -} - -// Separate lazy_static! blocks to avoid hitting some recursion limit when -// compiling -lazy_static! { - // Instance used for testing - pub static ref DEMO_INSTANCE_NAME: Name = "demo-instance".parse().unwrap(); - pub static ref DEMO_INSTANCE_SELECTOR: String = format!("{}&instance={}", *DEMO_PROJECT_SELECTOR, *DEMO_INSTANCE_NAME); - pub static ref DEMO_INSTANCE_URL: String = - format!("/v1/instances/{}?{}", *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR); - pub static ref DEMO_INSTANCE_START_URL: String = - format!("/v1/instances/{}/start?{}", *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR); - pub static ref DEMO_INSTANCE_STOP_URL: String = - format!("/v1/instances/{}/stop?{}", *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR); - pub static ref DEMO_INSTANCE_REBOOT_URL: String = - format!("/v1/instances/{}/reboot?{}", *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR); - pub static ref DEMO_INSTANCE_MIGRATE_URL: String = - format!("/v1/instances/{}/migrate?{}", *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR); - pub static ref DEMO_INSTANCE_SERIAL_URL: String = - format!("/v1/instances/{}/serial-console?{}", *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR); - pub static ref DEMO_INSTANCE_SERIAL_STREAM_URL: String = - format!("/v1/instances/{}/serial-console/stream?{}", *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR); - - pub static ref DEMO_INSTANCE_DISKS_URL: String = - format!("/v1/instances/{}/disks?{}", *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR); - pub static ref DEMO_INSTANCE_DISKS_ATTACH_URL: String = - format!("/v1/instances/{}/disks/attach?{}", *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR); - pub static ref DEMO_INSTANCE_DISKS_DETACH_URL: String = - format!("/v1/instances/{}/disks/detach?{}", *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR); - - pub static ref DEMO_INSTANCE_NICS_URL: String = - format!("/v1/network-interfaces?project={}&instance={}", *DEMO_PROJECT_NAME, *DEMO_INSTANCE_NAME); - pub static ref DEMO_INSTANCE_EXTERNAL_IPS_URL: String = - format!("/v1/instances/{}/external-ips?{}", *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR); - pub static ref DEMO_INSTANCE_CREATE: params::InstanceCreate = - params::InstanceCreate { - identity: IdentityMetadataCreateParams { - name: DEMO_INSTANCE_NAME.clone(), - description: String::from(""), - }, - ncpus: InstanceCpuCount(1), - memory: ByteCount::from_gibibytes_u32(16), - hostname: String::from("demo-instance"), - user_data: vec![], - network_interfaces: - params::InstanceNetworkInterfaceAttachment::Default, - external_ips: vec![ - params::ExternalIpCreate::Ephemeral { pool_name: Some(DEMO_IP_POOL_NAME.clone()) } - ], - disks: vec![], - start: true, - }; - - // The instance needs a network interface, too. - pub static ref DEMO_INSTANCE_NIC_NAME: Name = - nexus_defaults::DEFAULT_PRIMARY_NIC_NAME.parse().unwrap(); - pub static ref DEMO_INSTANCE_NIC_URL: String = - format!("/v1/network-interfaces/{}?project={}&instance={}", *DEMO_INSTANCE_NIC_NAME, *DEMO_PROJECT_NAME, *DEMO_INSTANCE_NAME); - pub static ref DEMO_INSTANCE_NIC_CREATE: params::InstanceNetworkInterfaceCreate = - params::InstanceNetworkInterfaceCreate { - identity: IdentityMetadataCreateParams { - name: DEMO_INSTANCE_NIC_NAME.clone(), - description: String::from(""), - }, - vpc_name: DEMO_VPC_NAME.clone(), - subnet_name: DEMO_VPC_SUBNET_NAME.clone(), - ip: None, - }; - pub static ref DEMO_INSTANCE_NIC_PUT: params::InstanceNetworkInterfaceUpdate = { - params::InstanceNetworkInterfaceUpdate { - identity: IdentityMetadataUpdateParams { - name: None, - description: Some(String::from("an updated description")), - }, - primary: false, } - }; -} - -lazy_static! { - pub static ref DEMO_CERTIFICATE_NAME: Name = - "demo-certificate".parse().unwrap(); - pub static ref DEMO_CERTIFICATES_URL: String = format!("/v1/certificates"); - pub static ref DEMO_CERTIFICATE_URL: String = - format!("/v1/certificates/demo-certificate"); - pub static ref DEMO_CERTIFICATE: CertificateChain = - CertificateChain::new(format!("*.sys.{DNS_ZONE_EXTERNAL_TESTING}")); - pub static ref DEMO_CERTIFICATE_CREATE: params::CertificateCreate = - params::CertificateCreate { - identity: IdentityMetadataCreateParams { - name: DEMO_CERTIFICATE_NAME.clone(), - description: String::from(""), - }, - cert: DEMO_CERTIFICATE.cert_chain_as_pem(), - key: DEMO_CERTIFICATE.end_cert_private_key_as_pem(), - service: shared::ServiceUsingCertificate::ExternalApi, - }; -} - -lazy_static! { - pub static ref DEMO_SWITCH_PORT_URL: String = - format!("/v1/system/hardware/switch-port"); - - pub static ref DEMO_SWITCH_PORT_SETTINGS_APPLY_URL: String = + }); +pub static DEMO_IMPORT_DISK_BULK_WRITE_START_URL: Lazy = + Lazy::new(|| { format!( - "/v1/system/hardware/switch-port/qsfp7/settings?rack_id={}&switch_location={}", - uuid::Uuid::new_v4(), - "switch0", - ); - - pub static ref DEMO_SWITCH_PORT_SETTINGS: params::SwitchPortApplySettings = - params::SwitchPortApplySettings { - port_settings: NameOrId::Name("portofino".parse().unwrap()), - }; -} - -lazy_static! { - pub static ref DEMO_LOOPBACK_CREATE_URL: String = - "/v1/system/networking/loopback-address".into(); - pub static ref DEMO_LOOPBACK_URL: String = format!( + "/v1/disks/{}/bulk-write-start?{}", + *DEMO_IMPORT_DISK_NAME, *DEMO_PROJECT_SELECTOR + ) + }); +pub static DEMO_IMPORT_DISK_BULK_WRITE_URL: Lazy = Lazy::new(|| { + format!( + "/v1/disks/{}/bulk-write?{}", + *DEMO_IMPORT_DISK_NAME, *DEMO_PROJECT_SELECTOR + ) +}); +pub static DEMO_IMPORT_DISK_BULK_WRITE_STOP_URL: Lazy = + Lazy::new(|| { + format!( + "/v1/disks/{}/bulk-write-stop?{}", + *DEMO_IMPORT_DISK_NAME, *DEMO_PROJECT_SELECTOR + ) + }); +pub static DEMO_IMPORT_DISK_FINALIZE_URL: Lazy = Lazy::new(|| { + format!( + "/v1/disks/{}/finalize?{}", + *DEMO_IMPORT_DISK_NAME, *DEMO_PROJECT_SELECTOR + ) +}); + +// Instance used for testing +pub static DEMO_INSTANCE_NAME: Lazy = + Lazy::new(|| "demo-instance".parse().unwrap()); +pub static DEMO_INSTANCE_URL: Lazy = Lazy::new(|| { + format!("/v1/instances/{}?{}", *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR) +}); +pub static DEMO_INSTANCE_START_URL: Lazy = Lazy::new(|| { + format!( + "/v1/instances/{}/start?{}", + *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR + ) +}); +pub static DEMO_INSTANCE_STOP_URL: Lazy = Lazy::new(|| { + format!( + "/v1/instances/{}/stop?{}", + *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR + ) +}); +pub static DEMO_INSTANCE_REBOOT_URL: Lazy = Lazy::new(|| { + format!( + "/v1/instances/{}/reboot?{}", + *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR + ) +}); +pub static DEMO_INSTANCE_MIGRATE_URL: Lazy = Lazy::new(|| { + format!( + "/v1/instances/{}/migrate?{}", + *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR + ) +}); +pub static DEMO_INSTANCE_SERIAL_URL: Lazy = Lazy::new(|| { + format!( + "/v1/instances/{}/serial-console?{}", + *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR + ) +}); +pub static DEMO_INSTANCE_SERIAL_STREAM_URL: Lazy = Lazy::new(|| { + format!( + "/v1/instances/{}/serial-console/stream?{}", + *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR + ) +}); +pub static DEMO_INSTANCE_DISKS_URL: Lazy = Lazy::new(|| { + format!( + "/v1/instances/{}/disks?{}", + *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR + ) +}); +pub static DEMO_INSTANCE_DISKS_ATTACH_URL: Lazy = Lazy::new(|| { + format!( + "/v1/instances/{}/disks/attach?{}", + *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR + ) +}); +pub static DEMO_INSTANCE_DISKS_DETACH_URL: Lazy = Lazy::new(|| { + format!( + "/v1/instances/{}/disks/detach?{}", + *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR + ) +}); +pub static DEMO_INSTANCE_NICS_URL: Lazy = Lazy::new(|| { + format!( + "/v1/network-interfaces?project={}&instance={}", + *DEMO_PROJECT_NAME, *DEMO_INSTANCE_NAME + ) +}); +pub static DEMO_INSTANCE_EXTERNAL_IPS_URL: Lazy = Lazy::new(|| { + format!( + "/v1/instances/{}/external-ips?{}", + *DEMO_INSTANCE_NAME, *DEMO_PROJECT_SELECTOR + ) +}); +pub static DEMO_INSTANCE_CREATE: Lazy = + Lazy::new(|| params::InstanceCreate { + identity: IdentityMetadataCreateParams { + name: DEMO_INSTANCE_NAME.clone(), + description: String::from(""), + }, + ncpus: InstanceCpuCount(1), + memory: ByteCount::from_gibibytes_u32(16), + hostname: String::from("demo-instance"), + user_data: vec![], + network_interfaces: params::InstanceNetworkInterfaceAttachment::Default, + external_ips: vec![params::ExternalIpCreate::Ephemeral { + pool_name: Some(DEMO_IP_POOL_NAME.clone()), + }], + disks: vec![], + start: true, + }); + +// The instance needs a network interface, too. +pub static DEMO_INSTANCE_NIC_NAME: Lazy = + Lazy::new(|| nexus_defaults::DEFAULT_PRIMARY_NIC_NAME.parse().unwrap()); +pub static DEMO_INSTANCE_NIC_URL: Lazy = Lazy::new(|| { + format!( + "/v1/network-interfaces/{}?project={}&instance={}", + *DEMO_INSTANCE_NIC_NAME, *DEMO_PROJECT_NAME, *DEMO_INSTANCE_NAME + ) +}); +pub static DEMO_INSTANCE_NIC_CREATE: Lazy< + params::InstanceNetworkInterfaceCreate, +> = Lazy::new(|| params::InstanceNetworkInterfaceCreate { + identity: IdentityMetadataCreateParams { + name: DEMO_INSTANCE_NIC_NAME.clone(), + description: String::from(""), + }, + vpc_name: DEMO_VPC_NAME.clone(), + subnet_name: DEMO_VPC_SUBNET_NAME.clone(), + ip: None, +}); +pub static DEMO_INSTANCE_NIC_PUT: Lazy = + Lazy::new(|| params::InstanceNetworkInterfaceUpdate { + identity: IdentityMetadataUpdateParams { + name: None, + description: Some(String::from("an updated description")), + }, + primary: false, + }); + +pub static DEMO_CERTIFICATE_NAME: Lazy = + Lazy::new(|| "demo-certificate".parse().unwrap()); +pub const DEMO_CERTIFICATES_URL: &'static str = "/v1/certificates"; +pub const DEMO_CERTIFICATE_URL: &'static str = + "/v1/certificates/demo-certificate"; +pub static DEMO_CERTIFICATE: Lazy = Lazy::new(|| { + CertificateChain::new(format!("*.sys.{DNS_ZONE_EXTERNAL_TESTING}")) +}); +pub static DEMO_CERTIFICATE_CREATE: Lazy = + Lazy::new(|| params::CertificateCreate { + identity: IdentityMetadataCreateParams { + name: DEMO_CERTIFICATE_NAME.clone(), + description: String::from(""), + }, + cert: DEMO_CERTIFICATE.cert_chain_as_pem(), + key: DEMO_CERTIFICATE.end_cert_private_key_as_pem(), + service: shared::ServiceUsingCertificate::ExternalApi, + }); + +pub const DEMO_SWITCH_PORT_URL: &'static str = + "/v1/system/hardware/switch-port"; +pub static DEMO_SWITCH_PORT_SETTINGS_APPLY_URL: Lazy = Lazy::new( + || { + format!( + "/v1/system/hardware/switch-port/qsfp7/settings?rack_id={}&switch_location={}", + uuid::Uuid::new_v4(), + "switch0", + ) + }, +); +pub static DEMO_SWITCH_PORT_SETTINGS: Lazy = + Lazy::new(|| params::SwitchPortApplySettings { + port_settings: NameOrId::Name("portofino".parse().unwrap()), + }); + +pub static DEMO_LOOPBACK_CREATE_URL: Lazy = + Lazy::new(|| "/v1/system/networking/loopback-address".into()); +pub static DEMO_LOOPBACK_URL: Lazy = Lazy::new(|| { + format!( "/v1/system/networking/loopback-address/{}/{}/{}", uuid::Uuid::new_v4(), "switch0", "203.0.113.99/24", - ); - pub static ref DEMO_LOOPBACK_CREATE: params::LoopbackAddressCreate = - params::LoopbackAddressCreate { - address_lot: NameOrId::Name("parkinglot".parse().unwrap()), - rack_id: uuid::Uuid::new_v4(), - switch_location: "switch0".parse().unwrap(), - address: "203.0.113.99".parse().unwrap(), - mask: 24, - anycast: false, - }; -} - -lazy_static! { - pub static ref DEMO_SWITCH_PORT_SETTINGS_URL: String = format!( - "/v1/system/networking/switch-port-settings?port_settings=portofino" - ); - pub static ref DEMO_SWITCH_PORT_SETTINGS_INFO_URL: String = - format!("/v1/system/networking/switch-port-settings/protofino"); - pub static ref DEMO_SWITCH_PORT_SETTINGS_CREATE: params::SwitchPortSettingsCreate = - params::SwitchPortSettingsCreate::new(IdentityMetadataCreateParams { - name: "portofino".parse().unwrap(), - description: "just a port".into(), - }); -} - -lazy_static! { - pub static ref DEMO_ADDRESS_LOTS_URL: String = - format!("/v1/system/networking/address-lot"); - pub static ref DEMO_ADDRESS_LOT_URL: String = - format!("/v1/system/networking/address-lot/parkinglot"); - pub static ref DEMO_ADDRESS_LOT_BLOCKS_URL: String = - format!("/v1/system/networking/address-lot/parkinglot/blocks"); - pub static ref DEMO_ADDRESS_LOT_CREATE: params::AddressLotCreate = - params::AddressLotCreate { - identity: IdentityMetadataCreateParams { - name: "parkinglot".parse().unwrap(), - description: "an address parking lot".into(), - }, - kind: AddressLotKind::Infra, - blocks: vec![params::AddressLotBlockCreate { - first_address: "203.0.113.10".parse().unwrap(), - last_address: "203.0.113.20".parse().unwrap(), - }], - }; -} - -lazy_static! { - pub static ref DEMO_BGP_CONFIG_CREATE_URL: String = - format!("/v1/system/networking/bgp?name_or_id=as47"); - pub static ref DEMO_BGP_CONFIG: params::BgpConfigCreate = - params::BgpConfigCreate { - identity: IdentityMetadataCreateParams { - name: "as47".parse().unwrap(), - description: "BGP config for AS47".into(), - }, - bgp_announce_set_id: NameOrId::Name("instances".parse().unwrap()), - asn: 47, - vrf: None, - }; - pub static ref DEMO_BGP_ANNOUNCE_SET_URL: String = - format!("/v1/system/networking/bgp-announce?name_or_id=a-bag-of-addrs"); - pub static ref DEMO_BGP_ANNOUNCE: params::BgpAnnounceSetCreate = - params::BgpAnnounceSetCreate { - identity: IdentityMetadataCreateParams { - name: "a-bag-of-addrs".parse().unwrap(), - description: "a bag of addrs".into(), - }, - announcement: vec![params::BgpAnnouncementCreate { - address_lot_block: NameOrId::Name( - "some-block".parse().unwrap(), - ), - network: "10.0.0.0/16".parse().unwrap(), - }], - }; - pub static ref DEMO_BGP_STATUS_URL: String = - format!("/v1/system/networking/bgp-status"); - pub static ref DEMO_BGP_ROUTES_IPV4_URL: String = - format!("/v1/system/networking/bgp-routes-ipv4?asn=47"); -} + ) +}); +pub static DEMO_LOOPBACK_CREATE: Lazy = + Lazy::new(|| params::LoopbackAddressCreate { + address_lot: NameOrId::Name("parkinglot".parse().unwrap()), + rack_id: uuid::Uuid::new_v4(), + switch_location: "switch0".parse().unwrap(), + address: "203.0.113.99".parse().unwrap(), + mask: 24, + anycast: false, + }); + +pub const DEMO_SWITCH_PORT_SETTINGS_URL: &'static str = + "/v1/system/networking/switch-port-settings?port_settings=portofino"; +pub const DEMO_SWITCH_PORT_SETTINGS_INFO_URL: &'static str = + "/v1/system/networking/switch-port-settings/protofino"; +pub static DEMO_SWITCH_PORT_SETTINGS_CREATE: Lazy< + params::SwitchPortSettingsCreate, +> = Lazy::new(|| { + params::SwitchPortSettingsCreate::new(IdentityMetadataCreateParams { + name: "portofino".parse().unwrap(), + description: "just a port".into(), + }) +}); + +pub const DEMO_ADDRESS_LOTS_URL: &'static str = + "/v1/system/networking/address-lot"; +pub const DEMO_ADDRESS_LOT_URL: &'static str = + "/v1/system/networking/address-lot/parkinglot"; +pub const DEMO_ADDRESS_LOT_BLOCKS_URL: &'static str = + "/v1/system/networking/address-lot/parkinglot/blocks"; +pub static DEMO_ADDRESS_LOT_CREATE: Lazy = + Lazy::new(|| params::AddressLotCreate { + identity: IdentityMetadataCreateParams { + name: "parkinglot".parse().unwrap(), + description: "an address parking lot".into(), + }, + kind: AddressLotKind::Infra, + blocks: vec![params::AddressLotBlockCreate { + first_address: "203.0.113.10".parse().unwrap(), + last_address: "203.0.113.20".parse().unwrap(), + }], + }); + +pub const DEMO_BGP_CONFIG_CREATE_URL: &'static str = + "/v1/system/networking/bgp?name_or_id=as47"; +pub static DEMO_BGP_CONFIG: Lazy = + Lazy::new(|| params::BgpConfigCreate { + identity: IdentityMetadataCreateParams { + name: "as47".parse().unwrap(), + description: "BGP config for AS47".into(), + }, + bgp_announce_set_id: NameOrId::Name("instances".parse().unwrap()), + asn: 47, + vrf: None, + }); +pub const DEMO_BGP_ANNOUNCE_SET_URL: &'static str = + "/v1/system/networking/bgp-announce?name_or_id=a-bag-of-addrs"; +pub static DEMO_BGP_ANNOUNCE: Lazy = + Lazy::new(|| params::BgpAnnounceSetCreate { + identity: IdentityMetadataCreateParams { + name: "a-bag-of-addrs".parse().unwrap(), + description: "a bag of addrs".into(), + }, + announcement: vec![params::BgpAnnouncementCreate { + address_lot_block: NameOrId::Name("some-block".parse().unwrap()), + network: "10.0.0.0/16".parse().unwrap(), + }], + }); +pub const DEMO_BGP_STATUS_URL: &'static str = + "/v1/system/networking/bgp-status"; +pub const DEMO_BGP_ROUTES_IPV4_URL: &'static str = + "/v1/system/networking/bgp-routes-ipv4?asn=47"; + +// Project Images +pub static DEMO_IMAGE_NAME: Lazy = + Lazy::new(|| "demo-image".parse().unwrap()); +pub static DEMO_PROJECT_IMAGES_URL: Lazy = + Lazy::new(|| format!("/v1/images?project={}", *DEMO_PROJECT_NAME)); +pub static DEMO_PROJECT_IMAGE_URL: Lazy = Lazy::new(|| { + format!("/v1/images/{}?project={}", *DEMO_IMAGE_NAME, *DEMO_PROJECT_NAME) +}); +pub static DEMO_PROJECT_PROMOTE_IMAGE_URL: Lazy = Lazy::new(|| { + format!( + "/v1/images/{}/promote?project={}", + *DEMO_IMAGE_NAME, *DEMO_PROJECT_NAME + ) +}); + +pub static DEMO_SILO_DEMOTE_IMAGE_URL: Lazy = Lazy::new(|| { + format!( + "/v1/images/{}/demote?project={}", + *DEMO_IMAGE_NAME, *DEMO_PROJECT_NAME + ) +}); + +pub static DEMO_IMAGE_CREATE: Lazy = + Lazy::new(|| params::ImageCreate { + identity: IdentityMetadataCreateParams { + name: DEMO_IMAGE_NAME.clone(), + description: String::from(""), + }, + source: params::ImageSource::YouCanBootAnythingAsLongAsItsAlpine, + os: "fake-os".to_string(), + version: "1.0".to_string(), + }); + +// IP Pools +pub static DEMO_IP_POOLS_PROJ_URL: Lazy = + Lazy::new(|| format!("/v1/ip-pools?project={}", *DEMO_PROJECT_NAME)); +pub const DEMO_IP_POOLS_URL: &'static str = "/v1/system/ip-pools"; +pub static DEMO_IP_POOL_NAME: Lazy = + Lazy::new(|| "default".parse().unwrap()); +pub static DEMO_IP_POOL_CREATE: Lazy = + Lazy::new(|| params::IpPoolCreate { + identity: IdentityMetadataCreateParams { + name: DEMO_IP_POOL_NAME.clone(), + description: String::from("an IP pool"), + }, + silo: None, + is_default: true, + }); +pub static DEMO_IP_POOL_PROJ_URL: Lazy = Lazy::new(|| { + format!( + "/v1/ip-pools/{}?project={}", + *DEMO_IP_POOL_NAME, *DEMO_PROJECT_NAME + ) +}); +pub static DEMO_IP_POOL_URL: Lazy = + Lazy::new(|| format!("/v1/system/ip-pools/{}", *DEMO_IP_POOL_NAME)); +pub static DEMO_IP_POOL_UPDATE: Lazy = + Lazy::new(|| params::IpPoolUpdate { + identity: IdentityMetadataUpdateParams { + name: None, + description: Some(String::from("a new IP pool")), + }, + }); +pub static DEMO_IP_POOL_RANGE: Lazy = Lazy::new(|| { + IpRange::V4( + Ipv4Range::new( + std::net::Ipv4Addr::new(10, 0, 0, 0), + std::net::Ipv4Addr::new(10, 0, 0, 255), + ) + .unwrap(), + ) +}); +pub static DEMO_IP_POOL_RANGES_URL: Lazy = + Lazy::new(|| format!("{}/ranges", *DEMO_IP_POOL_URL)); +pub static DEMO_IP_POOL_RANGES_ADD_URL: Lazy = + Lazy::new(|| format!("{}/add", *DEMO_IP_POOL_RANGES_URL)); +pub static DEMO_IP_POOL_RANGES_DEL_URL: Lazy = + Lazy::new(|| format!("{}/remove", *DEMO_IP_POOL_RANGES_URL)); + +// IP Pools (Services) +pub const DEMO_IP_POOL_SERVICE_URL: &'static str = + "/v1/system/ip-pools-service"; +pub static DEMO_IP_POOL_SERVICE_RANGES_URL: Lazy = + Lazy::new(|| format!("{}/ranges", DEMO_IP_POOL_SERVICE_URL)); +pub static DEMO_IP_POOL_SERVICE_RANGES_ADD_URL: Lazy = + Lazy::new(|| format!("{}/add", *DEMO_IP_POOL_SERVICE_RANGES_URL)); +pub static DEMO_IP_POOL_SERVICE_RANGES_DEL_URL: Lazy = + Lazy::new(|| format!("{}/remove", *DEMO_IP_POOL_SERVICE_RANGES_URL)); + +// Snapshots +pub static DEMO_SNAPSHOT_NAME: Lazy = + Lazy::new(|| "demo-snapshot".parse().unwrap()); +pub static DEMO_SNAPSHOT_URL: Lazy = Lazy::new(|| { + format!( + "/v1/snapshots/{}?project={}", + *DEMO_SNAPSHOT_NAME, *DEMO_PROJECT_NAME + ) +}); +pub static DEMO_SNAPSHOT_CREATE: Lazy = + Lazy::new(|| params::SnapshotCreate { + identity: IdentityMetadataCreateParams { + name: DEMO_SNAPSHOT_NAME.clone(), + description: String::from(""), + }, + disk: DEMO_DISK_NAME.clone().into(), + }); -lazy_static! { - // Project Images - pub static ref DEMO_IMAGE_NAME: Name = "demo-image".parse().unwrap(); - pub static ref DEMO_PROJECT_IMAGES_URL: String = - format!("/v1/images?project={}", *DEMO_PROJECT_NAME); - pub static ref DEMO_PROJECT_IMAGE_URL: String = - format!("/v1/images/{}?project={}", *DEMO_IMAGE_NAME, *DEMO_PROJECT_NAME); - pub static ref DEMO_PROJECT_PROMOTE_IMAGE_URL: String = - format!("/v1/images/{}/promote?project={}", *DEMO_IMAGE_NAME, *DEMO_PROJECT_NAME); - pub static ref DEMO_SILO_DEMOTE_IMAGE_URL: String = - format!("/v1/images/{}/demote?project={}", *DEMO_IMAGE_NAME, *DEMO_PROJECT_NAME); - pub static ref DEMO_IMAGE_CREATE: params::ImageCreate = - params::ImageCreate { - identity: IdentityMetadataCreateParams { - name: DEMO_IMAGE_NAME.clone(), - description: String::from(""), - }, - source: params::ImageSource::YouCanBootAnythingAsLongAsItsAlpine, - os: "fake-os".to_string(), - version: "1.0".to_string() - }; - - // IP Pools - pub static ref DEMO_IP_POOLS_PROJ_URL: String = - format!("/v1/ip-pools?project={}", *DEMO_PROJECT_NAME); - pub static ref DEMO_IP_POOLS_URL: &'static str = "/v1/system/ip-pools"; - pub static ref DEMO_IP_POOL_NAME: Name = "default".parse().unwrap(); - pub static ref DEMO_IP_POOL_CREATE: params::IpPoolCreate = - params::IpPoolCreate { - identity: IdentityMetadataCreateParams { - name: DEMO_IP_POOL_NAME.clone(), - description: String::from("an IP pool"), - }, - silo: None, - is_default: true, - }; - pub static ref DEMO_IP_POOL_PROJ_URL: String = - format!("/v1/ip-pools/{}?project={}", *DEMO_IP_POOL_NAME, *DEMO_PROJECT_NAME); - pub static ref DEMO_IP_POOL_URL: String = format!("/v1/system/ip-pools/{}", *DEMO_IP_POOL_NAME); - pub static ref DEMO_IP_POOL_UPDATE: params::IpPoolUpdate = - params::IpPoolUpdate { - identity: IdentityMetadataUpdateParams { - name: None, - description: Some(String::from("a new IP pool")), - }, - }; - pub static ref DEMO_IP_POOL_RANGE: IpRange = IpRange::V4(Ipv4Range::new( - std::net::Ipv4Addr::new(10, 0, 0, 0), - std::net::Ipv4Addr::new(10, 0, 0, 255), - ).unwrap()); - pub static ref DEMO_IP_POOL_RANGES_URL: String = format!("{}/ranges", *DEMO_IP_POOL_URL); - pub static ref DEMO_IP_POOL_RANGES_ADD_URL: String = format!("{}/add", *DEMO_IP_POOL_RANGES_URL); - pub static ref DEMO_IP_POOL_RANGES_DEL_URL: String = format!("{}/remove", *DEMO_IP_POOL_RANGES_URL); - - // IP Pools (Services) - pub static ref DEMO_IP_POOL_SERVICE_URL: &'static str = "/v1/system/ip-pools-service"; - pub static ref DEMO_IP_POOL_SERVICE_RANGES_URL: String = format!("{}/ranges", *DEMO_IP_POOL_SERVICE_URL); - pub static ref DEMO_IP_POOL_SERVICE_RANGES_ADD_URL: String = format!("{}/add", *DEMO_IP_POOL_SERVICE_RANGES_URL); - pub static ref DEMO_IP_POOL_SERVICE_RANGES_DEL_URL: String = format!("{}/remove", *DEMO_IP_POOL_SERVICE_RANGES_URL); - - // Snapshots - pub static ref DEMO_SNAPSHOT_NAME: Name = "demo-snapshot".parse().unwrap(); - pub static ref DEMO_SNAPSHOT_URL: String = - format!("/v1/snapshots/{}?project={}", *DEMO_SNAPSHOT_NAME, *DEMO_PROJECT_NAME); - pub static ref DEMO_SNAPSHOT_CREATE: params::SnapshotCreate = - params::SnapshotCreate { - identity: IdentityMetadataCreateParams { - name: DEMO_SNAPSHOT_NAME.clone(), - description: String::from(""), - }, - disk: DEMO_DISK_NAME.clone().into(), - }; +// SSH keys +pub const DEMO_SSHKEYS_URL: &'static str = "/v1/me/ssh-keys"; +pub static DEMO_SSHKEY_NAME: Lazy = + Lazy::new(|| "aaaaa-ssh-key".parse().unwrap()); - // SSH keys - pub static ref DEMO_SSHKEYS_URL: &'static str = "/v1/me/ssh-keys"; - pub static ref DEMO_SSHKEY_NAME: Name = "aaaaa-ssh-key".parse().unwrap(); - pub static ref DEMO_SSHKEY_CREATE: params::SshKeyCreate = params::SshKeyCreate { +pub static DEMO_SSHKEY_CREATE: Lazy = + Lazy::new(|| params::SshKeyCreate { identity: IdentityMetadataCreateParams { name: DEMO_SSHKEY_NAME.clone(), description: "a demo key".to_string(), }, public_key: "AAAAAAAAAAAAAAA".to_string(), - }; + }); - pub static ref DEMO_SPECIFIC_SSHKEY_URL: String = - format!("{}/{}", *DEMO_SSHKEYS_URL, *DEMO_SSHKEY_NAME); +pub static DEMO_SPECIFIC_SSHKEY_URL: Lazy = + Lazy::new(|| format!("{}/{}", DEMO_SSHKEYS_URL, *DEMO_SSHKEY_NAME)); - // System update +// System update - pub static ref DEMO_SYSTEM_UPDATE_PARAMS: params::SystemUpdatePath = params::SystemUpdatePath { - version: SemverVersion::new(1,0,0), - }; -} +pub static DEMO_SYSTEM_UPDATE_PARAMS: Lazy = + Lazy::new(|| params::SystemUpdatePath { + version: SemverVersion::new(1, 0, 0), + }); -lazy_static! { - // Project Floating IPs - pub static ref DEMO_FLOAT_IP_NAME: Name = "float-ip".parse().unwrap(); - pub static ref DEMO_FLOAT_IP_URL: String = - format!("/v1/floating-ips/{}?project={}", *DEMO_FLOAT_IP_NAME, *DEMO_PROJECT_NAME); - pub static ref DEMO_FLOAT_IP_CREATE: params::FloatingIpCreate = - params::FloatingIpCreate { - identity: IdentityMetadataCreateParams { - name: DEMO_FLOAT_IP_NAME.clone(), - description: String::from("a new IP pool"), - }, - address: Some(std::net::Ipv4Addr::new(10, 0, 0, 141).into()), - pool: None, - }; -} +// Project Floating IPs +pub static DEMO_FLOAT_IP_NAME: Lazy = + Lazy::new(|| "float-ip".parse().unwrap()); -lazy_static! { - // Identity providers - pub static ref IDENTITY_PROVIDERS_URL: String = format!("/v1/system/identity-providers?silo=demo-silo"); - pub static ref SAML_IDENTITY_PROVIDERS_URL: String = format!("/v1/system/identity-providers/saml?silo=demo-silo"); +pub static DEMO_FLOAT_IP_URL: Lazy = Lazy::new(|| { + format!( + "/v1/floating-ips/{}?project={}", + *DEMO_FLOAT_IP_NAME, *DEMO_PROJECT_NAME + ) +}); - pub static ref DEMO_SAML_IDENTITY_PROVIDER_NAME: Name = "demo-saml-provider".parse().unwrap(); - pub static ref SPECIFIC_SAML_IDENTITY_PROVIDER_URL: String = format!("/v1/system/identity-providers/saml/{}?silo=demo-silo", *DEMO_SAML_IDENTITY_PROVIDER_NAME); +pub static DEMO_FLOAT_IP_CREATE: Lazy = + Lazy::new(|| params::FloatingIpCreate { + identity: IdentityMetadataCreateParams { + name: DEMO_FLOAT_IP_NAME.clone(), + description: String::from("a new IP pool"), + }, + address: Some(std::net::Ipv4Addr::new(10, 0, 0, 141).into()), + pool: None, + }); + +// Identity providers +pub const IDENTITY_PROVIDERS_URL: &'static str = + "/v1/system/identity-providers?silo=demo-silo"; +pub const SAML_IDENTITY_PROVIDERS_URL: &'static str = + "/v1/system/identity-providers/saml?silo=demo-silo"; +pub static DEMO_SAML_IDENTITY_PROVIDER_NAME: Lazy = + Lazy::new(|| "demo-saml-provider".parse().unwrap()); + +pub static SPECIFIC_SAML_IDENTITY_PROVIDER_URL: Lazy = + Lazy::new(|| { + format!( + "/v1/system/identity-providers/saml/{}?silo=demo-silo", + *DEMO_SAML_IDENTITY_PROVIDER_NAME + ) + }); - pub static ref SAML_IDENTITY_PROVIDER: params::SamlIdentityProviderCreate = - params::SamlIdentityProviderCreate { - identity: IdentityMetadataCreateParams { - name: DEMO_SAML_IDENTITY_PROVIDER_NAME.clone(), - description: "a demo provider".to_string(), - }, +pub static SAML_IDENTITY_PROVIDER: Lazy = + Lazy::new(|| params::SamlIdentityProviderCreate { + identity: IdentityMetadataCreateParams { + name: DEMO_SAML_IDENTITY_PROVIDER_NAME.clone(), + description: "a demo provider".to_string(), + }, - idp_metadata_source: params::IdpMetadataSource::Url { url: HTTP_SERVER.url("/descriptor").to_string() }, + idp_metadata_source: params::IdpMetadataSource::Url { + url: HTTP_SERVER.url("/descriptor").to_string(), + }, - idp_entity_id: "entity_id".to_string(), - sp_client_id: "client_id".to_string(), - acs_url: "http://acs".to_string(), - slo_url: "http://slo".to_string(), - technical_contact_email: "technical@fake".to_string(), + idp_entity_id: "entity_id".to_string(), + sp_client_id: "client_id".to_string(), + acs_url: "http://acs".to_string(), + slo_url: "http://slo".to_string(), + technical_contact_email: "technical@fake".to_string(), - signing_keypair: None, + signing_keypair: None, - group_attribute_name: None, - }; + group_attribute_name: None, + }); - pub static ref DEMO_SYSTEM_METRICS_URL: String = - format!( - "/v1/system/metrics/virtual_disk_space_provisioned?start_time={:?}&end_time={:?}", - Utc::now(), - Utc::now(), - ); +pub static DEMO_SYSTEM_METRICS_URL: Lazy = Lazy::new(|| { + format!( + "/v1/system/metrics/virtual_disk_space_provisioned?start_time={:?}&end_time={:?}", + Utc::now(), + Utc::now(), + ) +}); - pub static ref DEMO_SILO_METRICS_URL: String = - format!( - "/v1/metrics/virtual_disk_space_provisioned?start_time={:?}&end_time={:?}", - Utc::now(), - Utc::now(), - ); +pub static DEMO_SILO_METRICS_URL: Lazy = Lazy::new(|| { + format!( + "/v1/metrics/virtual_disk_space_provisioned?start_time={:?}&end_time={:?}", + Utc::now(), + Utc::now(), + ) +}); - // Users - pub static ref DEMO_USER_CREATE: params::UserCreate = params::UserCreate { +// Users +pub static DEMO_USER_CREATE: Lazy = + Lazy::new(|| params::UserCreate { external_id: params::UserId::from_str("dummy-user").unwrap(), password: params::UserPassword::LoginDisallowed, - }; -} + }); /// Describes an API endpoint to be verified by the "unauthorized" test /// @@ -773,1269 +919,1270 @@ impl AllowedMethod { } } -lazy_static! { - pub static ref URL_USERS_DB_INIT: String = - format!("/v1/system/users-builtin/{}", authn::USER_DB_INIT.name); - - /// List of endpoints to be verified - pub static ref VERIFY_ENDPOINTS: Vec = vec![ - // Global IAM policy - VerifyEndpoint { - url: &SYSTEM_POLICY_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value( - &shared::Policy:: { - role_assignments: vec![] - } - ).unwrap() - ), - ], - }, - - // IP Pools top-level endpoint - VerifyEndpoint { - url: &DEMO_IP_POOLS_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_IP_POOL_CREATE).unwrap() - ), - ], - }, - VerifyEndpoint { - url: &DEMO_IP_POOLS_PROJ_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get - ], - }, - - // Single IP Pool endpoint - VerifyEndpoint { - url: &DEMO_IP_POOL_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value(&*DEMO_IP_POOL_UPDATE).unwrap() - ), - AllowedMethod::Delete, - ], - }, - VerifyEndpoint { - url: &DEMO_IP_POOL_PROJ_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get - ], - }, - - // IP Pool ranges endpoint - VerifyEndpoint { - url: &DEMO_IP_POOL_RANGES_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get - ], - }, - - // IP Pool ranges/add endpoint - VerifyEndpoint { - url: &DEMO_IP_POOL_RANGES_ADD_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(&*DEMO_IP_POOL_RANGE).unwrap() - ), - ], - }, - - // IP Pool ranges/delete endpoint - VerifyEndpoint { - url: &DEMO_IP_POOL_RANGES_DEL_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(&*DEMO_IP_POOL_RANGE).unwrap() - ), - ], - }, - - // IP Pool endpoint (Oxide services) - VerifyEndpoint { - url: &DEMO_IP_POOL_SERVICE_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get - ], - }, - - // IP Pool ranges endpoint (Oxide services) - VerifyEndpoint { - url: &DEMO_IP_POOL_SERVICE_RANGES_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get - ], - }, - - // IP Pool ranges/add endpoint (Oxide services) - VerifyEndpoint { - url: &DEMO_IP_POOL_SERVICE_RANGES_ADD_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(&*DEMO_IP_POOL_RANGE).unwrap() - ), - ], - }, - - // IP Pool ranges/delete endpoint (Oxide services) - VerifyEndpoint { - url: &DEMO_IP_POOL_SERVICE_RANGES_DEL_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(&*DEMO_IP_POOL_RANGE).unwrap() - ), - ], - }, - - /* Silos */ - VerifyEndpoint { - url: "/v1/system/silos", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_SILO_CREATE).unwrap() - ) - ], - }, - VerifyEndpoint { - url: &DEMO_SILO_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - ], - }, - VerifyEndpoint { - url: &DEMO_SILO_POLICY_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value( - &shared::Policy:: { - role_assignments: vec![] - } - ).unwrap() - ), - ], - }, - VerifyEndpoint { - url: &DEMO_SILO_QUOTAS_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value( - params::SiloQuotasCreate::empty() - ).unwrap() - ) - ], - }, - VerifyEndpoint { - url: "/v1/system/silo-quotas", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get - ], - }, - VerifyEndpoint { - url: "/v1/policy", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value( - &shared::Policy:: { - role_assignments: vec![] - } - ).unwrap() - ), - ], - }, - - VerifyEndpoint { - url: "/v1/users", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, - - VerifyEndpoint { - url: "/v1/groups", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, - - VerifyEndpoint { - // non-existent UUID that will 404 - url: "/v1/groups/8d90b9a5-1cea-4a2b-9af4-71467dd33a04", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::GetNonexistent, - ], - }, - - VerifyEndpoint { - url: &DEMO_SILO_USERS_LIST_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ AllowedMethod::Get ], - }, - - VerifyEndpoint { - url: &DEMO_SILO_USERS_CREATE_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value( - &*DEMO_USER_CREATE - ).unwrap() - ), - ], - }, - - VerifyEndpoint { - url: &DEMO_SILO_USER_ID_GET_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, - - VerifyEndpoint { - url: &DEMO_SILO_USER_ID_DELETE_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Delete, - ], - }, - - VerifyEndpoint { - url: &DEMO_SILO_USER_ID_SET_PASSWORD_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::to_value( - params::UserPassword::LoginDisallowed - ).unwrap()), - ], - }, - - /* Projects */ - - // TODO-security TODO-correctness One thing that's a little strange - // here: we currently return a 404 if you attempt to create a Project - // inside an Organization and you're not authorized to do that. In an - // ideal world, we'd return a 403 if you can _see_ the Organization and - // a 404 if not. But we don't really know if you should be able to see - // the Organization. Right now, the only real way to tell that is if - // you have permissions on anything _inside_ the Organization, which is - // incredibly expensive to determine in general. - // TODO: reevaluate the above comment and the change to unprivileged_access below - VerifyEndpoint { - url: "/v1/projects", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_PROJECT_CREATE).unwrap() - ), - ], - }, - - VerifyEndpoint { - url: &DEMO_PROJECT_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - AllowedMethod::Put( - serde_json::to_value(params::ProjectUpdate{ - identity: IdentityMetadataUpdateParams { - name: None, - description: Some("different".to_string()) - }, - }).unwrap() - ), - ], - }, - - VerifyEndpoint { - url: &DEMO_PROJECT_POLICY_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value( - &shared::Policy:: { - role_assignments: vec![] - } - ).unwrap() - ), - ], - }, - - /* VPCs */ - VerifyEndpoint { - url: &DEMO_PROJECT_URL_VPCS, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_VPC_CREATE).unwrap() - ), - ], - }, - - VerifyEndpoint { - url: &DEMO_VPC_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value(¶ms::VpcUpdate { - identity: IdentityMetadataUpdateParams { - name: None, - description: Some("different".to_string()) - }, - dns_name: None, - }).unwrap() - ), - AllowedMethod::Delete, - ], - }, - - /* Firewall rules */ - VerifyEndpoint { - url: &DEMO_VPC_URL_FIREWALL_RULES, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value(VpcFirewallRuleUpdateParams { - rules: vec![], - }).unwrap() - ), - ], - }, - - /* VPC Subnets */ - VerifyEndpoint { - url: &DEMO_VPC_URL_SUBNETS, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_VPC_SUBNET_CREATE).unwrap() - ), - ], - }, - - VerifyEndpoint { - url: &DEMO_VPC_SUBNET_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value(¶ms::VpcSubnetUpdate { - identity: IdentityMetadataUpdateParams { - name: None, - description: Some("different".to_string()) - }, - }).unwrap() - ), - AllowedMethod::Delete, - ], - }, - - VerifyEndpoint { - url: &DEMO_VPC_SUBNET_INTERFACES_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, - - /* VPC Routers */ - - VerifyEndpoint { - url: &DEMO_VPC_URL_ROUTERS, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_VPC_ROUTER_CREATE).unwrap() - ), - ], - }, - - VerifyEndpoint { - url: &DEMO_VPC_ROUTER_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value(¶ms::VpcRouterUpdate { - identity: IdentityMetadataUpdateParams { - name: None, - description: Some("different".to_string()) - }, - }).unwrap() - ), - AllowedMethod::Delete, - ], - }, - - /* Router Routes */ - - VerifyEndpoint { - url: &DEMO_VPC_ROUTER_URL_ROUTES, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_ROUTER_ROUTE_CREATE).unwrap() - ), - ], - }, - - VerifyEndpoint { - url: &DEMO_ROUTER_ROUTE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value(¶ms::RouterRouteUpdate { - identity: IdentityMetadataUpdateParams { - name: None, - description: Some("different".to_string()) - }, - target: RouteTarget::Ip( - IpAddr::from(Ipv4Addr::new(127, 0, 0, 1))), - destination: RouteDestination::Subnet( - "loopback".parse().unwrap()), - }).unwrap() - ), - AllowedMethod::Delete, - ], - }, - - /* Disks */ - - VerifyEndpoint { - url: &DEMO_DISKS_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_DISK_CREATE).unwrap() - ), - ], - }, - - VerifyEndpoint { - url: &DEMO_DISK_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - ], - }, - - VerifyEndpoint { - url: &DEMO_DISK_METRICS_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, - - VerifyEndpoint { - url: &DEMO_INSTANCE_DISKS_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - ] - }, - - VerifyEndpoint { - url: &DEMO_INSTANCE_DISKS_ATTACH_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(params::DiskPath { - disk: DEMO_DISK_NAME.clone().into() - }).unwrap() - ) - ], - }, - - VerifyEndpoint { - url: &DEMO_INSTANCE_DISKS_DETACH_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(params::DiskPath { - disk: DEMO_DISK_NAME.clone().into() - }).unwrap() - ) - ], - }, - - VerifyEndpoint { - url: &DEMO_IMPORT_DISK_BULK_WRITE_START_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::value::Value::Null), - ], - }, - - VerifyEndpoint { - url: &DEMO_IMPORT_DISK_BULK_WRITE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(params::ImportBlocksBulkWrite { - offset: 0, - base64_encoded_data: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==".into(), - }).unwrap()), - ], - }, - - VerifyEndpoint { - url: &DEMO_IMPORT_DISK_BULK_WRITE_STOP_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::value::Value::Null), - ], - }, - - VerifyEndpoint { - url: &DEMO_IMPORT_DISK_FINALIZE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::from_str("{}").unwrap()), - ], - }, - - /* Project images */ - - VerifyEndpoint { - url: &DEMO_PROJECT_URL_IMAGES, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_IMAGE_CREATE).unwrap() - ), - ], - }, - - VerifyEndpoint { - url: &DEMO_PROJECT_IMAGE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - ], - }, - - VerifyEndpoint { - url: &DEMO_PROJECT_PROMOTE_IMAGE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::value::Value::Null), - ], - }, - - VerifyEndpoint { - url: &DEMO_SILO_DEMOTE_IMAGE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::value::Value::Null), - ], - }, - - /* Snapshots */ - - VerifyEndpoint { - url: &DEMO_PROJECT_URL_SNAPSHOTS, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(DEMO_SNAPSHOT_CREATE.clone()).unwrap(), - ) - ] - }, - - VerifyEndpoint { - url: &DEMO_SNAPSHOT_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - ] - }, - - /* Instances */ - VerifyEndpoint { - url: &DEMO_PROJECT_URL_INSTANCES, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_INSTANCE_CREATE).unwrap() - ), - ], - }, - - VerifyEndpoint { - url: &DEMO_INSTANCE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - ], - }, - - VerifyEndpoint { - url: &DEMO_INSTANCE_START_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::Value::Null) - ], - }, - VerifyEndpoint { - url: &DEMO_INSTANCE_STOP_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::Value::Null) - ], - }, - VerifyEndpoint { - url: &DEMO_INSTANCE_REBOOT_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::Value::Null) - ], - }, - VerifyEndpoint { - url: &DEMO_INSTANCE_MIGRATE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::to_value( - params::InstanceMigrate { - dst_sled_id: uuid::Uuid::new_v4(), +pub static URL_USERS_DB_INIT: Lazy = Lazy::new(|| { + format!("/v1/system/users-builtin/{}", authn::USER_DB_INIT.name) +}); + +/// List of endpoints to be verified +pub static VERIFY_ENDPOINTS: Lazy> = Lazy::new(|| { + vec![ + // Global IAM policy + VerifyEndpoint { + url: &SYSTEM_POLICY_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Put( + serde_json::to_value( + &shared::Policy:: { + role_assignments: vec![] } - ).unwrap()), - ], - }, - VerifyEndpoint { - url: &DEMO_INSTANCE_SERIAL_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::GetNonexistent // has required query parameters - ], - }, - VerifyEndpoint { - url: &DEMO_INSTANCE_SERIAL_STREAM_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::GetWebsocket - ], - }, - - /* Instance NICs */ - VerifyEndpoint { - url: &DEMO_INSTANCE_NICS_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_INSTANCE_NIC_CREATE).unwrap() - ), - ], - }, - - VerifyEndpoint { - url: &DEMO_INSTANCE_NIC_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - AllowedMethod::Put( - serde_json::to_value(&*DEMO_INSTANCE_NIC_PUT).unwrap() - ), - ], - }, - - /* Instance external IP addresses */ - VerifyEndpoint { - url: &DEMO_INSTANCE_EXTERNAL_IPS_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - /* IAM */ - - VerifyEndpoint { - url: "/v1/system/roles", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - VerifyEndpoint { - url: "/v1/system/roles/fleet.admin", - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: "/v1/system/users-builtin", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - VerifyEndpoint { - url: &URL_USERS_DB_INIT, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - /* Hardware */ - - VerifyEndpoint { - url: "/v1/system/hardware/racks", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: &HARDWARE_RACK_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: &HARDWARE_UNINITIALIZED_SLEDS, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: "/v1/system/hardware/sleds", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get, AllowedMethod::Post( - serde_json::to_value(&*DEMO_UNINITIALIZED_SLED).unwrap() - )], - }, - - VerifyEndpoint { - url: &SLED_INSTANCES_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: &HARDWARE_SLED_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: &HARDWARE_SLED_PROVISION_STATE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Put( - serde_json::to_value(&*DEMO_SLED_PROVISION_STATE).unwrap() - )], - }, - - VerifyEndpoint { - url: "/v1/system/hardware/switches", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - // TODO: Switches should be configured alongside sled agents during test setup - VerifyEndpoint { - url: &HARDWARE_SWITCH_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::GetNonexistent], - }, - - VerifyEndpoint { - url: &HARDWARE_DISK_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: &HARDWARE_SLED_DISK_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - /* Updates */ - - VerifyEndpoint { - url: "/v1/system/update/refresh", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Post( - serde_json::Value::Null - )], - }, - - VerifyEndpoint { - url: "/v1/system/update/version", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: "/v1/system/update/components", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: "/v1/system/update/updates", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - // TODO: make system update endpoints work instead of expecting 404 - - VerifyEndpoint { - url: "/v1/system/update/updates/1.0.0", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: "/v1/system/update/updates/1.0.0/components", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: "/v1/system/update/start", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Post( - serde_json::to_value(&*DEMO_SYSTEM_UPDATE_PARAMS).unwrap() - )], - }, - - VerifyEndpoint { - url: "/v1/system/update/stop", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Post( - serde_json::Value::Null - )], - }, - - VerifyEndpoint { - url: "/v1/system/update/deployments", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: "/v1/system/update/deployments/120bbb6f-660a-440c-8cb7-199be202ddff", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::GetNonexistent], - }, - - /* Metrics */ - - VerifyEndpoint { - url: &DEMO_SYSTEM_METRICS_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, - - VerifyEndpoint { - url: &DEMO_SILO_METRICS_URL, - visibility: Visibility::Public, - // unprivileged user has silo read, otherwise they wouldn't be able - // to do anything - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, - - /* Silo identity providers */ - - VerifyEndpoint { - url: &IDENTITY_PROVIDERS_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, - - VerifyEndpoint { - url: &SAML_IDENTITY_PROVIDERS_URL, - // The visibility here deserves some explanation. In order to - // create a real SAML identity provider for doing tests, we have to - // do it in a non-default Silo (because the default one does not - // support creating a SAML identity provider). But unprivileged - // users won't be able to see that Silo. So from their perspective, - // it's like an object in a container they can't see (which is what - // Visibility::Protected means). - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Post( - serde_json::to_value(&*SAML_IDENTITY_PROVIDER).unwrap(), - )], - }, - VerifyEndpoint { - url: &SPECIFIC_SAML_IDENTITY_PROVIDER_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - /* Misc */ - - VerifyEndpoint { - url: "/v1/me", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, - VerifyEndpoint { - url: "/v1/me/groups", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, - - /* SSH keys */ - - VerifyEndpoint { - url: &DEMO_SSHKEYS_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::Full, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_SSHKEY_CREATE).unwrap(), - ), - ], - }, - VerifyEndpoint { - url: &DEMO_SPECIFIC_SSHKEY_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::Full, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - ], - }, - - /* Certificates */ - VerifyEndpoint { - url: &DEMO_CERTIFICATES_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_CERTIFICATE_CREATE).unwrap(), - ), - ], - }, - VerifyEndpoint { - url: &DEMO_CERTIFICATE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - ], - }, - - /* External Networking */ - - VerifyEndpoint { - url: &DEMO_SWITCH_PORT_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, - - - VerifyEndpoint { - url: &DEMO_SWITCH_PORT_SETTINGS_APPLY_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Delete, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_SWITCH_PORT_SETTINGS).unwrap(), - ), - ], - }, - - VerifyEndpoint { - url: &DEMO_ADDRESS_LOTS_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(&*DEMO_ADDRESS_LOT_CREATE).unwrap(), - ), - AllowedMethod::Get - ], - }, - - VerifyEndpoint { - url: &DEMO_ADDRESS_LOT_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Delete, - ] - }, - - VerifyEndpoint { - url: &DEMO_ADDRESS_LOT_BLOCKS_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::GetNonexistent - ], - }, - - VerifyEndpoint { - url: &DEMO_LOOPBACK_CREATE_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(&*DEMO_LOOPBACK_CREATE).unwrap(), - ), - AllowedMethod::Get, - ], - }, - - VerifyEndpoint { - url: &DEMO_LOOPBACK_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Delete - ], - }, - - VerifyEndpoint { - url: &DEMO_SWITCH_PORT_SETTINGS_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value( - &*DEMO_SWITCH_PORT_SETTINGS_CREATE).unwrap(), - ), - AllowedMethod::Get, - AllowedMethod::Delete - ], - }, - - VerifyEndpoint { - url: &DEMO_SWITCH_PORT_SETTINGS_INFO_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::GetNonexistent - ], - }, - VerifyEndpoint { - url: &DEMO_BGP_CONFIG_CREATE_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(&*DEMO_BGP_CONFIG).unwrap(), - ), - AllowedMethod::Get, - AllowedMethod::Delete - ], - }, - - VerifyEndpoint { - url: &DEMO_BGP_ANNOUNCE_SET_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(&*DEMO_BGP_ANNOUNCE).unwrap(), - ), - AllowedMethod::GetNonexistent, - AllowedMethod::Delete - ], - }, - - VerifyEndpoint { - url: &DEMO_BGP_STATUS_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::GetNonexistent, - ], - }, - - VerifyEndpoint { - url: &DEMO_BGP_ROUTES_IPV4_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::GetNonexistent, - ], - }, - - // Floating IPs - VerifyEndpoint { - url: &DEMO_PROJECT_URL_FIPS, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(&*DEMO_FLOAT_IP_CREATE).unwrap(), - ), - AllowedMethod::Get, - ], - }, - - VerifyEndpoint { - url: &DEMO_FLOAT_IP_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - ], - } - ]; -} + ).unwrap() + ), + ], + }, + + // IP Pools top-level endpoint + VerifyEndpoint { + url: &DEMO_IP_POOLS_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Post( + serde_json::to_value(&*DEMO_IP_POOL_CREATE).unwrap() + ), + ], + }, + VerifyEndpoint { + url: &DEMO_IP_POOLS_PROJ_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::ReadOnly, + allowed_methods: vec![ + AllowedMethod::Get + ], + }, + + // Single IP Pool endpoint + VerifyEndpoint { + url: &DEMO_IP_POOL_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Put( + serde_json::to_value(&*DEMO_IP_POOL_UPDATE).unwrap() + ), + AllowedMethod::Delete, + ], + }, + VerifyEndpoint { + url: &DEMO_IP_POOL_PROJ_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::ReadOnly, + allowed_methods: vec![ + AllowedMethod::Get + ], + }, + + // IP Pool ranges endpoint + VerifyEndpoint { + url: &DEMO_IP_POOL_RANGES_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get + ], + }, + + // IP Pool ranges/add endpoint + VerifyEndpoint { + url: &DEMO_IP_POOL_RANGES_ADD_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value(&*DEMO_IP_POOL_RANGE).unwrap() + ), + ], + }, + + // IP Pool ranges/delete endpoint + VerifyEndpoint { + url: &DEMO_IP_POOL_RANGES_DEL_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value(&*DEMO_IP_POOL_RANGE).unwrap() + ), + ], + }, + + // IP Pool endpoint (Oxide services) + VerifyEndpoint { + url: &DEMO_IP_POOL_SERVICE_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get + ], + }, + + // IP Pool ranges endpoint (Oxide services) + VerifyEndpoint { + url: &DEMO_IP_POOL_SERVICE_RANGES_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get + ], + }, + + // IP Pool ranges/add endpoint (Oxide services) + VerifyEndpoint { + url: &DEMO_IP_POOL_SERVICE_RANGES_ADD_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value(&*DEMO_IP_POOL_RANGE).unwrap() + ), + ], + }, + + // IP Pool ranges/delete endpoint (Oxide services) + VerifyEndpoint { + url: &DEMO_IP_POOL_SERVICE_RANGES_DEL_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value(&*DEMO_IP_POOL_RANGE).unwrap() + ), + ], + }, + + /* Silos */ + VerifyEndpoint { + url: "/v1/system/silos", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Post( + serde_json::to_value(&*DEMO_SILO_CREATE).unwrap() + ) + ], + }, + VerifyEndpoint { + url: &DEMO_SILO_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Delete, + ], + }, + VerifyEndpoint { + url: &DEMO_SILO_POLICY_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Put( + serde_json::to_value( + &shared::Policy:: { + role_assignments: vec![] + } + ).unwrap() + ), + ], + }, + VerifyEndpoint { + url: &DEMO_SILO_QUOTAS_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Put( + serde_json::to_value( + params::SiloQuotasCreate::empty() + ).unwrap() + ) + ], + }, + VerifyEndpoint { + url: "/v1/system/silo-quotas", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get + ], + }, + VerifyEndpoint { + url: "/v1/policy", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::ReadOnly, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Put( + serde_json::to_value( + &shared::Policy:: { + role_assignments: vec![] + } + ).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: "/v1/users", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::ReadOnly, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + + VerifyEndpoint { + url: "/v1/groups", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::ReadOnly, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + + VerifyEndpoint { + // non-existent UUID that will 404 + url: "/v1/groups/8d90b9a5-1cea-4a2b-9af4-71467dd33a04", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::ReadOnly, + allowed_methods: vec![ + AllowedMethod::GetNonexistent, + ], + }, + + VerifyEndpoint { + url: &DEMO_SILO_USERS_LIST_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::ReadOnly, + allowed_methods: vec![ AllowedMethod::Get ], + }, + + VerifyEndpoint { + url: &DEMO_SILO_USERS_CREATE_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::ReadOnly, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value( + &*DEMO_USER_CREATE + ).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_SILO_USER_ID_GET_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::ReadOnly, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + + VerifyEndpoint { + url: &DEMO_SILO_USER_ID_DELETE_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::ReadOnly, + allowed_methods: vec![ + AllowedMethod::Delete, + ], + }, + + VerifyEndpoint { + url: &DEMO_SILO_USER_ID_SET_PASSWORD_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::ReadOnly, + allowed_methods: vec![ + AllowedMethod::Post(serde_json::to_value( + params::UserPassword::LoginDisallowed + ).unwrap()), + ], + }, + + /* Projects */ + + // TODO-security TODO-correctness One thing that's a little strange + // here: we currently return a 404 if you attempt to create a Project + // inside an Organization and you're not authorized to do that. In an + // ideal world, we'd return a 403 if you can _see_ the Organization and + // a 404 if not. But we don't really know if you should be able to see + // the Organization. Right now, the only real way to tell that is if + // you have permissions on anything _inside_ the Organization, which is + // incredibly expensive to determine in general. + // TODO: reevaluate the above comment and the change to unprivileged_access below + VerifyEndpoint { + url: "/v1/projects", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Post( + serde_json::to_value(&*DEMO_PROJECT_CREATE).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_PROJECT_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Delete, + AllowedMethod::Put( + serde_json::to_value(params::ProjectUpdate{ + identity: IdentityMetadataUpdateParams { + name: None, + description: Some("different".to_string()) + }, + }).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_PROJECT_POLICY_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Put( + serde_json::to_value( + &shared::Policy:: { + role_assignments: vec![] + } + ).unwrap() + ), + ], + }, + + /* VPCs */ + VerifyEndpoint { + url: &DEMO_PROJECT_URL_VPCS, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Post( + serde_json::to_value(&*DEMO_VPC_CREATE).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_VPC_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Put( + serde_json::to_value(¶ms::VpcUpdate { + identity: IdentityMetadataUpdateParams { + name: None, + description: Some("different".to_string()) + }, + dns_name: None, + }).unwrap() + ), + AllowedMethod::Delete, + ], + }, + + /* Firewall rules */ + VerifyEndpoint { + url: &DEMO_VPC_URL_FIREWALL_RULES, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Put( + serde_json::to_value(VpcFirewallRuleUpdateParams { + rules: vec![], + }).unwrap() + ), + ], + }, + + /* VPC Subnets */ + VerifyEndpoint { + url: &DEMO_VPC_URL_SUBNETS, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Post( + serde_json::to_value(&*DEMO_VPC_SUBNET_CREATE).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_VPC_SUBNET_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Put( + serde_json::to_value(¶ms::VpcSubnetUpdate { + identity: IdentityMetadataUpdateParams { + name: None, + description: Some("different".to_string()) + }, + }).unwrap() + ), + AllowedMethod::Delete, + ], + }, + + VerifyEndpoint { + url: &DEMO_VPC_SUBNET_INTERFACES_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + + /* VPC Routers */ + + VerifyEndpoint { + url: &DEMO_VPC_URL_ROUTERS, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Post( + serde_json::to_value(&*DEMO_VPC_ROUTER_CREATE).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_VPC_ROUTER_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Put( + serde_json::to_value(¶ms::VpcRouterUpdate { + identity: IdentityMetadataUpdateParams { + name: None, + description: Some("different".to_string()) + }, + }).unwrap() + ), + AllowedMethod::Delete, + ], + }, + + /* Router Routes */ + + VerifyEndpoint { + url: &DEMO_VPC_ROUTER_URL_ROUTES, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Post( + serde_json::to_value(&*DEMO_ROUTER_ROUTE_CREATE).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_ROUTER_ROUTE_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Put( + serde_json::to_value(¶ms::RouterRouteUpdate { + identity: IdentityMetadataUpdateParams { + name: None, + description: Some("different".to_string()) + }, + target: RouteTarget::Ip( + IpAddr::from(Ipv4Addr::new(127, 0, 0, 1))), + destination: RouteDestination::Subnet( + "loopback".parse().unwrap()), + }).unwrap() + ), + AllowedMethod::Delete, + ], + }, + + /* Disks */ + + VerifyEndpoint { + url: &DEMO_DISKS_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Post( + serde_json::to_value(&*DEMO_DISK_CREATE).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_DISK_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Delete, + ], + }, + + VerifyEndpoint { + url: &DEMO_DISK_METRICS_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + + VerifyEndpoint { + url: &DEMO_INSTANCE_DISKS_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + ] + }, + + VerifyEndpoint { + url: &DEMO_INSTANCE_DISKS_ATTACH_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value(params::DiskPath { + disk: DEMO_DISK_NAME.clone().into() + }).unwrap() + ) + ], + }, + + VerifyEndpoint { + url: &DEMO_INSTANCE_DISKS_DETACH_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value(params::DiskPath { + disk: DEMO_DISK_NAME.clone().into() + }).unwrap() + ) + ], + }, + + VerifyEndpoint { + url: &DEMO_IMPORT_DISK_BULK_WRITE_START_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post(serde_json::value::Value::Null), + ], + }, + + VerifyEndpoint { + url: &DEMO_IMPORT_DISK_BULK_WRITE_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value(params::ImportBlocksBulkWrite { + offset: 0, + base64_encoded_data: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==".into(), + }).unwrap()), + ], + }, + + VerifyEndpoint { + url: &DEMO_IMPORT_DISK_BULK_WRITE_STOP_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post(serde_json::value::Value::Null), + ], + }, + + VerifyEndpoint { + url: &DEMO_IMPORT_DISK_FINALIZE_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post(serde_json::from_str("{}").unwrap()), + ], + }, + + /* Project images */ + + VerifyEndpoint { + url: &DEMO_PROJECT_URL_IMAGES, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Post( + serde_json::to_value(&*DEMO_IMAGE_CREATE).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_PROJECT_IMAGE_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Delete, + ], + }, + + VerifyEndpoint { + url: &DEMO_PROJECT_PROMOTE_IMAGE_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post(serde_json::value::Value::Null), + ], + }, + + VerifyEndpoint { + url: &DEMO_SILO_DEMOTE_IMAGE_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post(serde_json::value::Value::Null), + ], + }, + + /* Snapshots */ + + VerifyEndpoint { + url: &DEMO_PROJECT_URL_SNAPSHOTS, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Post( + serde_json::to_value(DEMO_SNAPSHOT_CREATE.clone()).unwrap(), + ) + ] + }, + + VerifyEndpoint { + url: &DEMO_SNAPSHOT_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Delete, + ] + }, + + /* Instances */ + VerifyEndpoint { + url: &DEMO_PROJECT_URL_INSTANCES, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Post( + serde_json::to_value(&*DEMO_INSTANCE_CREATE).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_INSTANCE_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Delete, + ], + }, + + VerifyEndpoint { + url: &DEMO_INSTANCE_START_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post(serde_json::Value::Null) + ], + }, + VerifyEndpoint { + url: &DEMO_INSTANCE_STOP_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post(serde_json::Value::Null) + ], + }, + VerifyEndpoint { + url: &DEMO_INSTANCE_REBOOT_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post(serde_json::Value::Null) + ], + }, + VerifyEndpoint { + url: &DEMO_INSTANCE_MIGRATE_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post(serde_json::to_value( + params::InstanceMigrate { + dst_sled_id: uuid::Uuid::new_v4(), + } + ).unwrap()), + ], + }, + VerifyEndpoint { + url: &DEMO_INSTANCE_SERIAL_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::GetNonexistent // has required query parameters + ], + }, + VerifyEndpoint { + url: &DEMO_INSTANCE_SERIAL_STREAM_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::GetWebsocket + ], + }, + + /* Instance NICs */ + VerifyEndpoint { + url: &DEMO_INSTANCE_NICS_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Post( + serde_json::to_value(&*DEMO_INSTANCE_NIC_CREATE).unwrap() + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_INSTANCE_NIC_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Delete, + AllowedMethod::Put( + serde_json::to_value(&*DEMO_INSTANCE_NIC_PUT).unwrap() + ), + ], + }, + + /* Instance external IP addresses */ + VerifyEndpoint { + url: &DEMO_INSTANCE_EXTERNAL_IPS_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + + /* IAM */ + + VerifyEndpoint { + url: "/v1/system/roles", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + VerifyEndpoint { + url: "/v1/system/roles/fleet.admin", + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + + VerifyEndpoint { + url: "/v1/system/users-builtin", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + VerifyEndpoint { + url: &URL_USERS_DB_INIT, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + + /* Hardware */ + + VerifyEndpoint { + url: "/v1/system/hardware/racks", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + + VerifyEndpoint { + url: &HARDWARE_RACK_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + + VerifyEndpoint { + url: &HARDWARE_UNINITIALIZED_SLEDS, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + + VerifyEndpoint { + url: "/v1/system/hardware/sleds", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get, AllowedMethod::Post( + serde_json::to_value(&*DEMO_UNINITIALIZED_SLED).unwrap() + )], + }, + + VerifyEndpoint { + url: &SLED_INSTANCES_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + + VerifyEndpoint { + url: &HARDWARE_SLED_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + + VerifyEndpoint { + url: &HARDWARE_SLED_PROVISION_STATE_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Put( + serde_json::to_value(&*DEMO_SLED_PROVISION_STATE).unwrap() + )], + }, + + VerifyEndpoint { + url: "/v1/system/hardware/switches", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + + // TODO: Switches should be configured alongside sled agents during test setup + VerifyEndpoint { + url: &HARDWARE_SWITCH_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::GetNonexistent], + }, + + VerifyEndpoint { + url: &HARDWARE_DISK_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + + VerifyEndpoint { + url: &HARDWARE_SLED_DISK_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + + /* Updates */ + + VerifyEndpoint { + url: "/v1/system/update/refresh", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Post( + serde_json::Value::Null + )], + }, + + VerifyEndpoint { + url: "/v1/system/update/version", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + + VerifyEndpoint { + url: "/v1/system/update/components", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + + VerifyEndpoint { + url: "/v1/system/update/updates", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + + // TODO: make system update endpoints work instead of expecting 404 + + VerifyEndpoint { + url: "/v1/system/update/updates/1.0.0", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + + VerifyEndpoint { + url: "/v1/system/update/updates/1.0.0/components", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + + VerifyEndpoint { + url: "/v1/system/update/start", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Post( + serde_json::to_value(&*DEMO_SYSTEM_UPDATE_PARAMS).unwrap() + )], + }, + + VerifyEndpoint { + url: "/v1/system/update/stop", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Post( + serde_json::Value::Null + )], + }, + + VerifyEndpoint { + url: "/v1/system/update/deployments", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + + VerifyEndpoint { + url: "/v1/system/update/deployments/120bbb6f-660a-440c-8cb7-199be202ddff", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::GetNonexistent], + }, + + /* Metrics */ + + VerifyEndpoint { + url: &DEMO_SYSTEM_METRICS_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + + VerifyEndpoint { + url: &DEMO_SILO_METRICS_URL, + visibility: Visibility::Public, + // unprivileged user has silo read, otherwise they wouldn't be able + // to do anything + unprivileged_access: UnprivilegedAccess::ReadOnly, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + + /* Silo identity providers */ + + VerifyEndpoint { + url: &IDENTITY_PROVIDERS_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::ReadOnly, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + + VerifyEndpoint { + url: &SAML_IDENTITY_PROVIDERS_URL, + // The visibility here deserves some explanation. In order to + // create a real SAML identity provider for doing tests, we have to + // do it in a non-default Silo (because the default one does not + // support creating a SAML identity provider). But unprivileged + // users won't be able to see that Silo. So from their perspective, + // it's like an object in a container they can't see (which is what + // Visibility::Protected means). + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Post( + serde_json::to_value(&*SAML_IDENTITY_PROVIDER).unwrap(), + )], + }, + VerifyEndpoint { + url: &SPECIFIC_SAML_IDENTITY_PROVIDER_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + + /* Misc */ + + VerifyEndpoint { + url: "/v1/me", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::ReadOnly, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + VerifyEndpoint { + url: "/v1/me/groups", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::ReadOnly, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + + /* SSH keys */ + + VerifyEndpoint { + url: &DEMO_SSHKEYS_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::Full, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Post( + serde_json::to_value(&*DEMO_SSHKEY_CREATE).unwrap(), + ), + ], + }, + VerifyEndpoint { + url: &DEMO_SPECIFIC_SSHKEY_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::Full, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Delete, + ], + }, + + /* Certificates */ + VerifyEndpoint { + url: &DEMO_CERTIFICATES_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Post( + serde_json::to_value(&*DEMO_CERTIFICATE_CREATE).unwrap(), + ), + ], + }, + VerifyEndpoint { + url: &DEMO_CERTIFICATE_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Delete, + ], + }, + + /* External Networking */ + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_SETTINGS_APPLY_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Delete, + AllowedMethod::Post( + serde_json::to_value(&*DEMO_SWITCH_PORT_SETTINGS).unwrap(), + ), + ], + }, + + VerifyEndpoint { + url: &DEMO_ADDRESS_LOTS_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value(&*DEMO_ADDRESS_LOT_CREATE).unwrap(), + ), + AllowedMethod::Get + ], + }, + + VerifyEndpoint { + url: &DEMO_ADDRESS_LOT_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Delete, + ] + }, + + VerifyEndpoint { + url: &DEMO_ADDRESS_LOT_BLOCKS_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::GetNonexistent + ], + }, + + VerifyEndpoint { + url: &DEMO_LOOPBACK_CREATE_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value(&*DEMO_LOOPBACK_CREATE).unwrap(), + ), + AllowedMethod::Get, + ], + }, + + VerifyEndpoint { + url: &DEMO_LOOPBACK_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Delete + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_SETTINGS_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value( + &*DEMO_SWITCH_PORT_SETTINGS_CREATE).unwrap(), + ), + AllowedMethod::Get, + AllowedMethod::Delete + ], + }, + + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_SETTINGS_INFO_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::GetNonexistent + ], + }, + VerifyEndpoint { + url: &DEMO_BGP_CONFIG_CREATE_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value(&*DEMO_BGP_CONFIG).unwrap(), + ), + AllowedMethod::Get, + AllowedMethod::Delete + ], + }, + + VerifyEndpoint { + url: &DEMO_BGP_ANNOUNCE_SET_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value(&*DEMO_BGP_ANNOUNCE).unwrap(), + ), + AllowedMethod::GetNonexistent, + AllowedMethod::Delete + ], + }, + + VerifyEndpoint { + url: &DEMO_BGP_STATUS_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::GetNonexistent, + ], + }, + + VerifyEndpoint { + url: &DEMO_BGP_ROUTES_IPV4_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::GetNonexistent, + ], + }, + + // Floating IPs + VerifyEndpoint { + url: &DEMO_PROJECT_URL_FIPS, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value(&*DEMO_FLOAT_IP_CREATE).unwrap(), + ), + AllowedMethod::Get, + ], + }, + + VerifyEndpoint { + url: &DEMO_FLOAT_IP_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Delete, + ], + } +] +}); diff --git a/nexus/tests/integration_tests/unauthorized.rs b/nexus/tests/integration_tests/unauthorized.rs index 1cb2eaca3a..317a5a0576 100644 --- a/nexus/tests/integration_tests/unauthorized.rs +++ b/nexus/tests/integration_tests/unauthorized.rs @@ -13,7 +13,6 @@ use headers::authorization::Credentials; use http::method::Method; use http::StatusCode; use httptest::{matchers::*, responders::*, Expectation, ServerBuilder}; -use lazy_static::lazy_static; use nexus_db_queries::authn::external::spoof; use nexus_test_utils::http_testing::AuthnMode; use nexus_test_utils::http_testing::NexusRequest; @@ -21,6 +20,7 @@ use nexus_test_utils::http_testing::RequestBuilder; use nexus_test_utils::http_testing::TestResponse; use nexus_test_utils::resource_helpers::DiskTest; use nexus_test_utils_macros::nexus_test; +use once_cell::sync::Lazy; type ControlPlaneTestContext = nexus_test_utils::ControlPlaneTestContext; @@ -158,8 +158,8 @@ enum SetupReq { }, } -lazy_static! { - pub static ref HTTP_SERVER: httptest::Server = { +pub static HTTP_SERVER: Lazy = + Lazy::new(|| { // Run a httptest server let server = ServerBuilder::new().run().unwrap(); @@ -167,12 +167,10 @@ lazy_static! { server.expect( Expectation::matching(request::method_path("HEAD", "/image.raw")) .times(1..) - .respond_with( - status_code(200).append_header( - "Content-Length", - format!("{}", 4096 * 1000), - ), - ), + .respond_with(status_code(200).append_header( + "Content-Length", + format!("{}", 4096 * 1000), + )), ); server.expect( @@ -182,10 +180,11 @@ lazy_static! { ); server - }; + }); - /// List of requests to execute at setup time - static ref SETUP_REQUESTS: Vec = vec![ +/// List of requests to execute at setup time +static SETUP_REQUESTS: Lazy> = Lazy::new(|| { + vec![ // Create a separate Silo SetupReq::Post { url: "/v1/system/silos", @@ -203,10 +202,7 @@ lazy_static! { ], }, // Get the default IP pool - SetupReq::Get { - url: &DEMO_IP_POOL_URL, - id_routes: vec![], - }, + SetupReq::Get { url: &DEMO_IP_POOL_URL, id_routes: vec![] }, // Create an IP pool range SetupReq::Post { url: &DEMO_IP_POOL_RANGES_ADD_URL, @@ -302,8 +298,8 @@ lazy_static! { body: serde_json::to_value(&*DEMO_CERTIFICATE_CREATE).unwrap(), id_routes: vec![], }, - ]; -} + ] +}); /// Contents returned from an endpoint that creates a resource that has an id /// From d01a7a5f24e1fd5f1ef0a88d170c8b1424a03c78 Mon Sep 17 00:00:00 2001 From: Sean Klein Date: Thu, 14 Dec 2023 14:55:32 -0800 Subject: [PATCH 2/3] Remove preprocessed_configs, again --- nexus/preprocessed_configs/config.xml | 41 --------------------------- 1 file changed, 41 deletions(-) delete mode 100644 nexus/preprocessed_configs/config.xml diff --git a/nexus/preprocessed_configs/config.xml b/nexus/preprocessed_configs/config.xml deleted file mode 100644 index 9b13f12aea..0000000000 --- a/nexus/preprocessed_configs/config.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - trace - true - - - 8123 - 9000 - 9004 - - ./ - - true - - - - - - - ::/0 - - - default - default - 1 - - - - - - - - - - - \ No newline at end of file From 0ba6500c03d5c095eacc9ccf0732f7b4a74d848b Mon Sep 17 00:00:00 2001 From: Sean Klein Date: Tue, 19 Dec 2023 21:38:59 -0800 Subject: [PATCH 3/3] Fix mis-merge in endpoints --- nexus/tests/integration_tests/endpoints.rs | 1665 ++++++-------------- 1 file changed, 472 insertions(+), 1193 deletions(-) diff --git a/nexus/tests/integration_tests/endpoints.rs b/nexus/tests/integration_tests/endpoints.rs index 5174fe6373..be0ea2a3f5 100644 --- a/nexus/tests/integration_tests/endpoints.rs +++ b/nexus/tests/integration_tests/endpoints.rs @@ -1682,1251 +1682,530 @@ pub static VERIFY_ENDPOINTS: Lazy> = Lazy::new(|| { params::InstanceMigrate { dst_sled_id: uuid::Uuid::new_v4(), } - ).unwrap() - ), - ], - }, - - // IP Pools top-level endpoint - VerifyEndpoint { - url: &DEMO_IP_POOLS_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_IP_POOL_CREATE).unwrap() - ), - ], - }, - VerifyEndpoint { - url: &DEMO_IP_POOLS_PROJ_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get - ], - }, - - // Single IP Pool endpoint - VerifyEndpoint { - url: &DEMO_IP_POOL_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value(&*DEMO_IP_POOL_UPDATE).unwrap() - ), - AllowedMethod::Delete, - ], - }, - VerifyEndpoint { - url: &DEMO_IP_POOL_PROJ_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get - ], - }, - - // IP Pool ranges endpoint - VerifyEndpoint { - url: &DEMO_IP_POOL_RANGES_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get - ], - }, - - // IP Pool ranges/add endpoint - VerifyEndpoint { - url: &DEMO_IP_POOL_RANGES_ADD_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(&*DEMO_IP_POOL_RANGE).unwrap() - ), - ], - }, + ).unwrap()), + ], + }, + VerifyEndpoint { + url: &DEMO_INSTANCE_SERIAL_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::GetNonexistent // has required query parameters + ], + }, + VerifyEndpoint { + url: &DEMO_INSTANCE_SERIAL_STREAM_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::GetWebsocket + ], + }, + /* Instance NICs */ + VerifyEndpoint { + url: &DEMO_INSTANCE_NICS_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Post( + serde_json::to_value(&*DEMO_INSTANCE_NIC_CREATE).unwrap() + ), + ], + }, + VerifyEndpoint { + url: &DEMO_INSTANCE_NIC_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Delete, + AllowedMethod::Put( + serde_json::to_value(&*DEMO_INSTANCE_NIC_PUT).unwrap() + ), + ], + }, - // IP Pool ranges/delete endpoint - VerifyEndpoint { - url: &DEMO_IP_POOL_RANGES_DEL_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(&*DEMO_IP_POOL_RANGE).unwrap() - ), - ], - }, + /* Instance external IP addresses */ + VerifyEndpoint { + url: &DEMO_INSTANCE_EXTERNAL_IPS_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, - // IP Pool endpoint (Oxide services) - VerifyEndpoint { - url: &DEMO_IP_POOL_SERVICE_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get - ], - }, + /* IAM */ - // IP Pool ranges endpoint (Oxide services) - VerifyEndpoint { - url: &DEMO_IP_POOL_SERVICE_RANGES_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get - ], - }, + VerifyEndpoint { + url: "/v1/system/roles", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + VerifyEndpoint { + url: "/v1/system/roles/fleet.admin", + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, - // IP Pool ranges/add endpoint (Oxide services) - VerifyEndpoint { - url: &DEMO_IP_POOL_SERVICE_RANGES_ADD_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(&*DEMO_IP_POOL_RANGE).unwrap() - ), - ], - }, + VerifyEndpoint { + url: "/v1/system/users-builtin", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + VerifyEndpoint { + url: &URL_USERS_DB_INIT, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, - // IP Pool ranges/delete endpoint (Oxide services) - VerifyEndpoint { - url: &DEMO_IP_POOL_SERVICE_RANGES_DEL_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(&*DEMO_IP_POOL_RANGE).unwrap() - ), - ], - }, + /* Hardware */ - /* Silos */ - VerifyEndpoint { - url: "/v1/system/silos", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_SILO_CREATE).unwrap() - ) - ], - }, - VerifyEndpoint { - url: &DEMO_SILO_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - ], - }, - VerifyEndpoint { - url: &DEMO_SILO_POLICY_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value( - &shared::Policy:: { - role_assignments: vec![] - } - ).unwrap() - ), - ], - }, - VerifyEndpoint { - url: &DEMO_SILO_QUOTAS_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value( - params::SiloQuotasCreate::empty() - ).unwrap() - ) - ], - }, - VerifyEndpoint { - url: "/v1/system/silo-quotas", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get - ], - }, - VerifyEndpoint { - url: "/v1/policy", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value( - &shared::Policy:: { - role_assignments: vec![] - } - ).unwrap() - ), - ], - }, + VerifyEndpoint { + url: "/v1/system/hardware/racks", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, - VerifyEndpoint { - url: "/v1/users", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, + VerifyEndpoint { + url: &HARDWARE_RACK_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, - VerifyEndpoint { - url: "/v1/groups", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, + VerifyEndpoint { + url: &HARDWARE_UNINITIALIZED_SLEDS, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, - VerifyEndpoint { - // non-existent UUID that will 404 - url: "/v1/groups/8d90b9a5-1cea-4a2b-9af4-71467dd33a04", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::GetNonexistent, - ], - }, + VerifyEndpoint { + url: "/v1/system/hardware/sleds", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get, AllowedMethod::Post( + serde_json::to_value(&*DEMO_UNINITIALIZED_SLED).unwrap() + )], + }, - VerifyEndpoint { - url: &DEMO_SILO_USERS_LIST_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ AllowedMethod::Get ], - }, + VerifyEndpoint { + url: &SLED_INSTANCES_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, - VerifyEndpoint { - url: &DEMO_SILO_USERS_CREATE_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value( - &*DEMO_USER_CREATE - ).unwrap() - ), - ], - }, + VerifyEndpoint { + url: &HARDWARE_SLED_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, - VerifyEndpoint { - url: &DEMO_SILO_USER_ID_GET_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, + VerifyEndpoint { + url: &HARDWARE_SLED_PROVISION_STATE_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Put( + serde_json::to_value(&*DEMO_SLED_PROVISION_STATE).unwrap() + )], + }, - VerifyEndpoint { - url: &DEMO_SILO_USER_ID_DELETE_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Delete, - ], - }, + VerifyEndpoint { + url: "/v1/system/hardware/switches", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, - VerifyEndpoint { - url: &DEMO_SILO_USER_ID_SET_PASSWORD_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::to_value( - params::UserPassword::LoginDisallowed - ).unwrap()), - ], - }, + // TODO: Switches should be configured alongside sled agents during test setup + VerifyEndpoint { + url: &HARDWARE_SWITCH_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::GetNonexistent], + }, - /* Projects */ - - // TODO-security TODO-correctness One thing that's a little strange - // here: we currently return a 404 if you attempt to create a Project - // inside an Organization and you're not authorized to do that. In an - // ideal world, we'd return a 403 if you can _see_ the Organization and - // a 404 if not. But we don't really know if you should be able to see - // the Organization. Right now, the only real way to tell that is if - // you have permissions on anything _inside_ the Organization, which is - // incredibly expensive to determine in general. - // TODO: reevaluate the above comment and the change to unprivileged_access below - VerifyEndpoint { - url: "/v1/projects", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_PROJECT_CREATE).unwrap() - ), - ], - }, + VerifyEndpoint { + url: &HARDWARE_DISK_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, - VerifyEndpoint { - url: &DEMO_PROJECT_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - AllowedMethod::Put( - serde_json::to_value(params::ProjectUpdate{ - identity: IdentityMetadataUpdateParams { - name: None, - description: Some("different".to_string()) - }, - }).unwrap() - ), - ], - }, + VerifyEndpoint { + url: &HARDWARE_SLED_DISK_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, - VerifyEndpoint { - url: &DEMO_PROJECT_POLICY_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value( - &shared::Policy:: { - role_assignments: vec![] - } - ).unwrap() - ), - ], - }, + /* Updates */ - /* VPCs */ - VerifyEndpoint { - url: &DEMO_PROJECT_URL_VPCS, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_VPC_CREATE).unwrap() - ), - ], - }, + VerifyEndpoint { + url: "/v1/system/update/refresh", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Post( + serde_json::Value::Null + )], + }, - VerifyEndpoint { - url: &DEMO_VPC_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value(¶ms::VpcUpdate { - identity: IdentityMetadataUpdateParams { - name: None, - description: Some("different".to_string()) - }, - dns_name: None, - }).unwrap() - ), - AllowedMethod::Delete, - ], - }, + VerifyEndpoint { + url: "/v1/system/update/version", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, - /* Firewall rules */ - VerifyEndpoint { - url: &DEMO_VPC_URL_FIREWALL_RULES, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value(VpcFirewallRuleUpdateParams { - rules: vec![], - }).unwrap() - ), - ], - }, + VerifyEndpoint { + url: "/v1/system/update/components", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, - /* VPC Subnets */ - VerifyEndpoint { - url: &DEMO_VPC_URL_SUBNETS, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_VPC_SUBNET_CREATE).unwrap() - ), - ], - }, + VerifyEndpoint { + url: "/v1/system/update/updates", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, - VerifyEndpoint { - url: &DEMO_VPC_SUBNET_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value(¶ms::VpcSubnetUpdate { - identity: IdentityMetadataUpdateParams { - name: None, - description: Some("different".to_string()) - }, - }).unwrap() - ), - AllowedMethod::Delete, - ], - }, + // TODO: make system update endpoints work instead of expecting 404 - VerifyEndpoint { - url: &DEMO_VPC_SUBNET_INTERFACES_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, + VerifyEndpoint { + url: "/v1/system/update/updates/1.0.0", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, - /* VPC Routers */ + VerifyEndpoint { + url: "/v1/system/update/updates/1.0.0/components", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, - VerifyEndpoint { - url: &DEMO_VPC_URL_ROUTERS, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_VPC_ROUTER_CREATE).unwrap() - ), - ], - }, + VerifyEndpoint { + url: "/v1/system/update/start", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Post( + serde_json::to_value(&*DEMO_SYSTEM_UPDATE_PARAMS).unwrap() + )], + }, - VerifyEndpoint { - url: &DEMO_VPC_ROUTER_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value(¶ms::VpcRouterUpdate { - identity: IdentityMetadataUpdateParams { - name: None, - description: Some("different".to_string()) - }, - }).unwrap() - ), - AllowedMethod::Delete, - ], - }, + VerifyEndpoint { + url: "/v1/system/update/stop", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Post( + serde_json::Value::Null + )], + }, - /* Router Routes */ + VerifyEndpoint { + url: "/v1/system/update/deployments", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, - VerifyEndpoint { - url: &DEMO_VPC_ROUTER_URL_ROUTES, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_ROUTER_ROUTE_CREATE).unwrap() - ), - ], - }, + VerifyEndpoint { + url: "/v1/system/update/deployments/120bbb6f-660a-440c-8cb7-199be202ddff", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::GetNonexistent], + }, - VerifyEndpoint { - url: &DEMO_ROUTER_ROUTE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Put( - serde_json::to_value(¶ms::RouterRouteUpdate { - identity: IdentityMetadataUpdateParams { - name: None, - description: Some("different".to_string()) - }, - target: RouteTarget::Ip( - IpAddr::from(Ipv4Addr::new(127, 0, 0, 1))), - destination: RouteDestination::Subnet( - "loopback".parse().unwrap()), - }).unwrap() - ), - AllowedMethod::Delete, - ], - }, + /* Metrics */ - /* Disks */ + VerifyEndpoint { + url: &DEMO_SYSTEM_METRICS_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, - VerifyEndpoint { - url: &DEMO_DISKS_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_DISK_CREATE).unwrap() - ), - ], - }, + VerifyEndpoint { + url: &DEMO_SILO_METRICS_URL, + visibility: Visibility::Public, + // unprivileged user has silo read, otherwise they wouldn't be able + // to do anything + unprivileged_access: UnprivilegedAccess::ReadOnly, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, - VerifyEndpoint { - url: &DEMO_DISK_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - ], - }, + /* Silo identity providers */ - VerifyEndpoint { - url: &DEMO_DISK_METRICS_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, + VerifyEndpoint { + url: &IDENTITY_PROVIDERS_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::ReadOnly, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, - VerifyEndpoint { - url: &DEMO_INSTANCE_DISKS_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - ] - }, + VerifyEndpoint { + url: &SAML_IDENTITY_PROVIDERS_URL, + // The visibility here deserves some explanation. In order to + // create a real SAML identity provider for doing tests, we have to + // do it in a non-default Silo (because the default one does not + // support creating a SAML identity provider). But unprivileged + // users won't be able to see that Silo. So from their perspective, + // it's like an object in a container they can't see (which is what + // Visibility::Protected means). + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Post( + serde_json::to_value(&*SAML_IDENTITY_PROVIDER).unwrap(), + )], + }, + VerifyEndpoint { + url: &SPECIFIC_SAML_IDENTITY_PROVIDER_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![AllowedMethod::Get], + }, + /* Misc */ - VerifyEndpoint { - url: &DEMO_INSTANCE_DISKS_ATTACH_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(params::DiskPath { - disk: DEMO_DISK_NAME.clone().into() - }).unwrap() - ) - ], - }, + VerifyEndpoint { + url: "/v1/me", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::ReadOnly, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, + VerifyEndpoint { + url: "/v1/me/groups", + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::ReadOnly, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, - VerifyEndpoint { - url: &DEMO_INSTANCE_DISKS_DETACH_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(params::DiskPath { - disk: DEMO_DISK_NAME.clone().into() - }).unwrap() - ) - ], - }, + /* SSH keys */ - VerifyEndpoint { - url: &DEMO_IMPORT_DISK_BULK_WRITE_START_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::value::Value::Null), - ], - }, - - VerifyEndpoint { - url: &DEMO_IMPORT_DISK_BULK_WRITE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(params::ImportBlocksBulkWrite { - offset: 0, - base64_encoded_data: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==".into(), - }).unwrap()), - ], - }, - - VerifyEndpoint { - url: &DEMO_IMPORT_DISK_BULK_WRITE_STOP_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::value::Value::Null), - ], - }, - - VerifyEndpoint { - url: &DEMO_IMPORT_DISK_FINALIZE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::from_str("{}").unwrap()), - ], - }, - - /* Project images */ - - VerifyEndpoint { - url: &DEMO_PROJECT_URL_IMAGES, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_IMAGE_CREATE).unwrap() - ), - ], - }, - - VerifyEndpoint { - url: &DEMO_PROJECT_IMAGE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - ], - }, - - VerifyEndpoint { - url: &DEMO_PROJECT_PROMOTE_IMAGE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::value::Value::Null), - ], - }, - - VerifyEndpoint { - url: &DEMO_SILO_DEMOTE_IMAGE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::value::Value::Null), - ], - }, - - /* Snapshots */ - - VerifyEndpoint { - url: &DEMO_PROJECT_URL_SNAPSHOTS, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(DEMO_SNAPSHOT_CREATE.clone()).unwrap(), - ) - ] - }, - - VerifyEndpoint { - url: &DEMO_SNAPSHOT_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - ] - }, - - /* Instances */ - VerifyEndpoint { - url: &DEMO_PROJECT_URL_INSTANCES, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_INSTANCE_CREATE).unwrap() - ), - ], - }, - - VerifyEndpoint { - url: &DEMO_INSTANCE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - ], - }, - - VerifyEndpoint { - url: &DEMO_INSTANCE_START_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::Value::Null) - ], - }, - VerifyEndpoint { - url: &DEMO_INSTANCE_STOP_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::Value::Null) - ], - }, - VerifyEndpoint { - url: &DEMO_INSTANCE_REBOOT_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::Value::Null) - ], - }, - VerifyEndpoint { - url: &DEMO_INSTANCE_MIGRATE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post(serde_json::to_value( - params::InstanceMigrate { - dst_sled_id: uuid::Uuid::new_v4(), - } - ).unwrap()), - ], - }, - VerifyEndpoint { - url: &DEMO_INSTANCE_SERIAL_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::GetNonexistent // has required query parameters - ], - }, - VerifyEndpoint { - url: &DEMO_INSTANCE_SERIAL_STREAM_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::GetWebsocket - ], - }, - - /* Instance NICs */ - VerifyEndpoint { - url: &DEMO_INSTANCE_NICS_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_INSTANCE_NIC_CREATE).unwrap() - ), - ], - }, - - VerifyEndpoint { - url: &DEMO_INSTANCE_NIC_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - AllowedMethod::Put( - serde_json::to_value(&*DEMO_INSTANCE_NIC_PUT).unwrap() - ), - ], - }, - - /* Instance external IP addresses */ - VerifyEndpoint { - url: &DEMO_INSTANCE_EXTERNAL_IPS_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - /* IAM */ - - VerifyEndpoint { - url: "/v1/system/roles", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - VerifyEndpoint { - url: "/v1/system/roles/fleet.admin", - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: "/v1/system/users-builtin", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - VerifyEndpoint { - url: &URL_USERS_DB_INIT, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - /* Hardware */ - - VerifyEndpoint { - url: "/v1/system/hardware/racks", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: &HARDWARE_RACK_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: &HARDWARE_UNINITIALIZED_SLEDS, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: "/v1/system/hardware/sleds", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get, AllowedMethod::Post( - serde_json::to_value(&*DEMO_UNINITIALIZED_SLED).unwrap() - )], - }, - - VerifyEndpoint { - url: &SLED_INSTANCES_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: &HARDWARE_SLED_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: &HARDWARE_SLED_PROVISION_STATE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Put( - serde_json::to_value(&*DEMO_SLED_PROVISION_STATE).unwrap() - )], - }, - - VerifyEndpoint { - url: "/v1/system/hardware/switches", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - // TODO: Switches should be configured alongside sled agents during test setup - VerifyEndpoint { - url: &HARDWARE_SWITCH_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::GetNonexistent], - }, - - VerifyEndpoint { - url: &HARDWARE_DISK_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: &HARDWARE_SLED_DISK_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - /* Updates */ - - VerifyEndpoint { - url: "/v1/system/update/refresh", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Post( - serde_json::Value::Null - )], - }, - - VerifyEndpoint { - url: "/v1/system/update/version", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: "/v1/system/update/components", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: "/v1/system/update/updates", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - // TODO: make system update endpoints work instead of expecting 404 - - VerifyEndpoint { - url: "/v1/system/update/updates/1.0.0", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: "/v1/system/update/updates/1.0.0/components", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: "/v1/system/update/start", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Post( - serde_json::to_value(&*DEMO_SYSTEM_UPDATE_PARAMS).unwrap() - )], - }, - - VerifyEndpoint { - url: "/v1/system/update/stop", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Post( - serde_json::Value::Null - )], - }, - - VerifyEndpoint { - url: "/v1/system/update/deployments", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - VerifyEndpoint { - url: "/v1/system/update/deployments/120bbb6f-660a-440c-8cb7-199be202ddff", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::GetNonexistent], - }, - - /* Metrics */ - - VerifyEndpoint { - url: &DEMO_SYSTEM_METRICS_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, - - VerifyEndpoint { - url: &DEMO_SILO_METRICS_URL, - visibility: Visibility::Public, - // unprivileged user has silo read, otherwise they wouldn't be able - // to do anything - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, - - /* Silo identity providers */ - - VerifyEndpoint { - url: &IDENTITY_PROVIDERS_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, - - VerifyEndpoint { - url: &SAML_IDENTITY_PROVIDERS_URL, - // The visibility here deserves some explanation. In order to - // create a real SAML identity provider for doing tests, we have to - // do it in a non-default Silo (because the default one does not - // support creating a SAML identity provider). But unprivileged - // users won't be able to see that Silo. So from their perspective, - // it's like an object in a container they can't see (which is what - // Visibility::Protected means). - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Post( - serde_json::to_value(&*SAML_IDENTITY_PROVIDER).unwrap(), - )], - }, - VerifyEndpoint { - url: &SPECIFIC_SAML_IDENTITY_PROVIDER_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![AllowedMethod::Get], - }, - - /* Misc */ - - VerifyEndpoint { - url: "/v1/me", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, - VerifyEndpoint { - url: "/v1/me/groups", - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::ReadOnly, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, - - /* SSH keys */ - - VerifyEndpoint { - url: &DEMO_SSHKEYS_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::Full, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_SSHKEY_CREATE).unwrap(), - ), - ], - }, - VerifyEndpoint { - url: &DEMO_SPECIFIC_SSHKEY_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::Full, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - ], - }, + VerifyEndpoint { + url: &DEMO_SSHKEYS_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::Full, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Post( + serde_json::to_value(&*DEMO_SSHKEY_CREATE).unwrap(), + ), + ], + }, + VerifyEndpoint { + url: &DEMO_SPECIFIC_SSHKEY_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::Full, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Delete, + ], + }, - /* Certificates */ - VerifyEndpoint { - url: &DEMO_CERTIFICATES_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_CERTIFICATE_CREATE).unwrap(), - ), - ], - }, - VerifyEndpoint { - url: &DEMO_CERTIFICATE_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - ], - }, + /* Certificates */ + VerifyEndpoint { + url: &DEMO_CERTIFICATES_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Post( + serde_json::to_value(&*DEMO_CERTIFICATE_CREATE).unwrap(), + ), + ], + }, + VerifyEndpoint { + url: &DEMO_CERTIFICATE_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Delete, + ], + }, - /* External Networking */ + /* External Networking */ - VerifyEndpoint { - url: &DEMO_SWITCH_PORT_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - ], - }, + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + ], + }, - VerifyEndpoint { - url: &DEMO_SWITCH_PORT_SETTINGS_APPLY_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Delete, - AllowedMethod::Post( - serde_json::to_value(&*DEMO_SWITCH_PORT_SETTINGS).unwrap(), - ), - ], - }, + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_SETTINGS_APPLY_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Delete, + AllowedMethod::Post( + serde_json::to_value(&*DEMO_SWITCH_PORT_SETTINGS).unwrap(), + ), + ], + }, - VerifyEndpoint { - url: &DEMO_ADDRESS_LOTS_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(&*DEMO_ADDRESS_LOT_CREATE).unwrap(), - ), - AllowedMethod::Get - ], - }, + VerifyEndpoint { + url: &DEMO_ADDRESS_LOTS_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value(&*DEMO_ADDRESS_LOT_CREATE).unwrap(), + ), + AllowedMethod::Get + ], + }, - VerifyEndpoint { - url: &DEMO_ADDRESS_LOT_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Delete, - ] - }, + VerifyEndpoint { + url: &DEMO_ADDRESS_LOT_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Delete, + ] + }, - VerifyEndpoint { - url: &DEMO_ADDRESS_LOT_BLOCKS_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::GetNonexistent - ], - }, + VerifyEndpoint { + url: &DEMO_ADDRESS_LOT_BLOCKS_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::GetNonexistent + ], + }, - VerifyEndpoint { - url: &DEMO_LOOPBACK_CREATE_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(&*DEMO_LOOPBACK_CREATE).unwrap(), - ), - AllowedMethod::Get, - ], - }, + VerifyEndpoint { + url: &DEMO_LOOPBACK_CREATE_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value(&*DEMO_LOOPBACK_CREATE).unwrap(), + ), + AllowedMethod::Get, + ], + }, - VerifyEndpoint { - url: &DEMO_LOOPBACK_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Delete - ], - }, + VerifyEndpoint { + url: &DEMO_LOOPBACK_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Delete + ], + }, - VerifyEndpoint { - url: &DEMO_SWITCH_PORT_SETTINGS_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value( - &*DEMO_SWITCH_PORT_SETTINGS_CREATE).unwrap(), - ), - AllowedMethod::Get, - AllowedMethod::Delete - ], - }, + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_SETTINGS_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value( + &*DEMO_SWITCH_PORT_SETTINGS_CREATE).unwrap(), + ), + AllowedMethod::Get, + AllowedMethod::Delete + ], + }, - VerifyEndpoint { - url: &DEMO_SWITCH_PORT_SETTINGS_INFO_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::GetNonexistent - ], - }, - VerifyEndpoint { - url: &DEMO_BGP_CONFIG_CREATE_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(&*DEMO_BGP_CONFIG).unwrap(), - ), - AllowedMethod::Get, - AllowedMethod::Delete - ], - }, + VerifyEndpoint { + url: &DEMO_SWITCH_PORT_SETTINGS_INFO_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::GetNonexistent + ], + }, + VerifyEndpoint { + url: &DEMO_BGP_CONFIG_CREATE_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value(&*DEMO_BGP_CONFIG).unwrap(), + ), + AllowedMethod::Get, + AllowedMethod::Delete + ], + }, - VerifyEndpoint { - url: &DEMO_BGP_ANNOUNCE_SET_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(&*DEMO_BGP_ANNOUNCE).unwrap(), - ), - AllowedMethod::GetNonexistent, - AllowedMethod::Delete - ], - }, + VerifyEndpoint { + url: &DEMO_BGP_ANNOUNCE_SET_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value(&*DEMO_BGP_ANNOUNCE).unwrap(), + ), + AllowedMethod::GetNonexistent, + AllowedMethod::Delete + ], + }, - VerifyEndpoint { - url: &DEMO_BGP_STATUS_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::GetNonexistent, - ], - }, + VerifyEndpoint { + url: &DEMO_BGP_STATUS_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::GetNonexistent, + ], + }, - VerifyEndpoint { - url: &DEMO_BGP_ROUTES_IPV4_URL, - visibility: Visibility::Public, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::GetNonexistent, - ], - }, + VerifyEndpoint { + url: &DEMO_BGP_ROUTES_IPV4_URL, + visibility: Visibility::Public, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::GetNonexistent, + ], + }, - // Floating IPs - VerifyEndpoint { - url: &DEMO_PROJECT_URL_FIPS, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Post( - serde_json::to_value(&*DEMO_FLOAT_IP_CREATE).unwrap(), - ), - AllowedMethod::Get, - ], - }, + // Floating IPs + VerifyEndpoint { + url: &DEMO_PROJECT_URL_FIPS, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Post( + serde_json::to_value(&*DEMO_FLOAT_IP_CREATE).unwrap(), + ), + AllowedMethod::Get, + ], + }, - VerifyEndpoint { - url: &DEMO_FLOAT_IP_URL, - visibility: Visibility::Protected, - unprivileged_access: UnprivilegedAccess::None, - allowed_methods: vec![ - AllowedMethod::Get, - AllowedMethod::Delete, - ], - } -] + VerifyEndpoint { + url: &DEMO_FLOAT_IP_URL, + visibility: Visibility::Protected, + unprivileged_access: UnprivilegedAccess::None, + allowed_methods: vec![ + AllowedMethod::Get, + AllowedMethod::Delete, + ], + }, + ] });