diff --git a/crates/icp-cli/src/commands/deploy/mod.rs b/crates/icp-cli/src/commands/deploy.rs similarity index 96% rename from crates/icp-cli/src/commands/deploy/mod.rs rename to crates/icp-cli/src/commands/deploy.rs index 5a13a511..324df93e 100644 --- a/crates/icp-cli/src/commands/deploy/mod.rs +++ b/crates/icp-cli/src/commands/deploy.rs @@ -3,7 +3,6 @@ use candid::{CandidType, Principal}; use clap::Args; use futures::{StreamExt, future::try_join_all, stream::FuturesOrdered}; use ic_agent::Agent; -use icp::network::{Managed, ManagedMode}; use icp::parsers::CyclesAmount; use icp::{ context::{CanisterSelection, Context, EnvironmentSelection}, @@ -385,24 +384,16 @@ async fn print_canister_urls( let env = ctx.get_environment(environment_selection).await?; // Get the network URL - let http_gateway_url = match &env.network.configuration { + let (http_gateway_url, has_friendly) = match &env.network.configuration { NetworkConfiguration::Managed { managed: _ } => { let access = ctx.network.access(&env.network).await?; - access.http_gateway_url.clone() + (access.http_gateway_url.clone(), access.use_friendly_domains) + } + NetworkConfiguration::Connected { connected } => { + (connected.http_gateway_url.clone(), false) } - NetworkConfiguration::Connected { connected } => connected.http_gateway_url.clone(), }; - // Friendly domains are available for managed networks where we write custom-domains.txt - let has_friendly = matches!( - &env.network.configuration, - NetworkConfiguration::Managed { - managed: Managed { - mode: ManagedMode::Launcher(config) - } - } if config.version.is_none() - ); - let _ = ctx.term.write_line("\n\nDeployed canisters:"); for name in canister_names { diff --git a/crates/icp/src/context/tests.rs b/crates/icp/src/context/tests.rs index bfd73115..2008c954 100644 --- a/crates/icp/src/context/tests.rs +++ b/crates/icp/src/context/tests.rs @@ -351,6 +351,7 @@ async fn test_get_agent_for_env_uses_environment_network() { root_key: local_root_key.clone(), api_url: Url::parse("http://localhost:8000").unwrap(), http_gateway_url: None, + use_friendly_domains: false, }, ) .with_network( @@ -359,6 +360,7 @@ async fn test_get_agent_for_env_uses_environment_network() { root_key: staging_root_key.clone(), api_url: Url::parse("http://staging:9000").unwrap(), http_gateway_url: None, + use_friendly_domains: false, }, ), ), @@ -432,6 +434,7 @@ async fn test_get_agent_for_network_success() { root_key: root_key.clone(), api_url: Url::parse("http://localhost:8000").unwrap(), http_gateway_url: None, + use_friendly_domains: false, }, )), ..Context::mocked() @@ -645,6 +648,7 @@ async fn test_get_agent_defaults_inside_project_with_default_local() { root_key: local_root_key.clone(), api_url: Url::parse(DEFAULT_LOCAL_NETWORK_URL).unwrap(), http_gateway_url: None, + use_friendly_domains: false, }, )), ..Context::mocked() @@ -717,6 +721,7 @@ async fn test_get_agent_defaults_with_overridden_local_network() { root_key: custom_root_key.clone(), api_url: Url::parse("http://localhost:9000").unwrap(), // Custom port http_gateway_url: None, + use_friendly_domains: false, }, )), ..Context::mocked() @@ -816,6 +821,7 @@ async fn test_get_agent_defaults_with_overridden_local_environment() { root_key: local_root_key.clone(), api_url: Url::parse(DEFAULT_LOCAL_NETWORK_URL).unwrap(), http_gateway_url: None, + use_friendly_domains: false, }, ) .with_network( @@ -824,6 +830,7 @@ async fn test_get_agent_defaults_with_overridden_local_environment() { root_key: custom_root_key.clone(), api_url: Url::parse("http://localhost:7000").unwrap(), http_gateway_url: None, + use_friendly_domains: false, }, ), ), @@ -858,6 +865,7 @@ async fn test_get_agent_explicit_network_inside_project() { root_key: local_root_key.clone(), api_url: Url::parse(DEFAULT_LOCAL_NETWORK_URL).unwrap(), http_gateway_url: None, + use_friendly_domains: false, }, ) .with_network( @@ -866,6 +874,7 @@ async fn test_get_agent_explicit_network_inside_project() { root_key: staging_root_key.clone(), api_url: Url::parse("http://localhost:8001").unwrap(), http_gateway_url: None, + use_friendly_domains: false, }, ), ), @@ -901,6 +910,7 @@ async fn test_get_agent_explicit_environment_inside_project() { root_key: local_root_key.clone(), api_url: Url::parse(DEFAULT_LOCAL_NETWORK_URL).unwrap(), http_gateway_url: None, + use_friendly_domains: false, }, ) .with_network( @@ -909,6 +919,7 @@ async fn test_get_agent_explicit_environment_inside_project() { root_key: staging_root_key.clone(), api_url: Url::parse("http://localhost:8001").unwrap(), http_gateway_url: None, + use_friendly_domains: false, }, ), ), diff --git a/crates/icp/src/network/access.rs b/crates/icp/src/network/access.rs index 67b85493..6a7616db 100644 --- a/crates/icp/src/network/access.rs +++ b/crates/icp/src/network/access.rs @@ -13,8 +13,10 @@ pub struct NetworkAccess { /// Routing configuration pub api_url: Url, - pub http_gateway_url: Option, + + /// If true, use friendly canister names with the gateway url + pub use_friendly_domains: bool, } #[derive(Debug, Snafu)] @@ -81,6 +83,7 @@ pub async fn get_managed_network_access( root_key: desc.root_key, api_url: http_gateway_url.clone(), http_gateway_url: Some(http_gateway_url), + use_friendly_domains: desc.use_friendly_domains, }) } @@ -93,5 +96,6 @@ pub async fn get_connected_network_access( root_key, api_url: connected.api_url.clone(), http_gateway_url: connected.http_gateway_url.clone(), + use_friendly_domains: false, }) } diff --git a/crates/icp/src/network/config.rs b/crates/icp/src/network/config.rs index cbf25f8f..cc663ff4 100644 --- a/crates/icp/src/network/config.rs +++ b/crates/icp/src/network/config.rs @@ -79,6 +79,9 @@ pub struct NetworkDescriptorModel { /// Used to write `custom-domains.txt` for friendly domain routing. #[serde(default)] pub status_dir: Option, + /// Whether the network supports friendly domain routing (e.g., `foo.local.localhost`). + #[serde(default)] + pub use_friendly_domains: bool, } /// Identifies the process or container running a managed network. diff --git a/crates/icp/src/network/managed/docker.rs b/crates/icp/src/network/managed/docker.rs index 350de695..c43c2b8f 100644 --- a/crates/icp/src/network/managed/docker.rs +++ b/crates/icp/src/network/managed/docker.rs @@ -19,7 +19,9 @@ use tokio::select; use wslpath2::Conversion; use crate::network::{ - ManagedImageConfig, config::ChildLocator, managed::launcher::NetworkInstance, + ManagedImageConfig, + config::ChildLocator, + managed::launcher::{CUSTOM_DOMAINS_FEATURE, NetworkInstance}, }; use crate::prelude::*; @@ -499,6 +501,10 @@ pub async fn spawn_docker_launcher( root_key: hex::decode(&launcher_status.root_key).context(ParseRootKeySnafu { key: &launcher_status.root_key, })?, + use_friendly_domains: launcher_status + .supported_features + .iter() + .any(|f| f == CUSTOM_DOMAINS_FEATURE), }, locator, gateway_port_was_fixed, diff --git a/crates/icp/src/network/managed/launcher.rs b/crates/icp/src/network/managed/launcher.rs index 4d5b46b5..34a66baa 100644 --- a/crates/icp/src/network/managed/launcher.rs +++ b/crates/icp/src/network/managed/launcher.rs @@ -18,6 +18,7 @@ pub struct NetworkInstance { pub root_key: Vec, pub pocketic_config_port: Option, pub pocketic_instance_id: Option, + pub use_friendly_domains: bool, } #[derive(Debug, Snafu)] @@ -141,6 +142,10 @@ pub async fn spawn_network_launcher( })?, pocketic_config_port: launcher_status.config_port, pocketic_instance_id: launcher_status.instance_id, + use_friendly_domains: launcher_status + .supported_features + .iter() + .any(|f| f == CUSTOM_DOMAINS_FEATURE), }, ChildLocator::Pid { pid, start_time }, )) @@ -357,8 +362,12 @@ pub struct LauncherStatus { pub gateway_port: u16, pub root_key: String, pub default_effective_canister_id: Option, + #[serde(default)] + pub supported_features: Vec, } +pub const CUSTOM_DOMAINS_FEATURE: &str = "custom-domains"; + struct WatchRecv(Sender>); impl EventHandler for WatchRecv { diff --git a/crates/icp/src/network/managed/run.rs b/crates/icp/src/network/managed/run.rs index 209281b5..f90c8e90 100644 --- a/crates/icp/src/network/managed/run.rs +++ b/crates/icp/src/network/managed/run.rs @@ -274,6 +274,7 @@ async fn run_network_launcher( candid_ui_canister_id, proxy_canister_id, status_dir: Some(status_dir_path.clone()), + use_friendly_domains: instance.use_friendly_domains, }; // Save descriptor to project root and all fixed port directories