Skip to content

Commit 154b201

Browse files
authored
[localnet] Add support for using existing Docker network (#18079)
1 parent e7b2c9d commit 154b201

File tree

5 files changed

+72
-6
lines changed

5 files changed

+72
-6
lines changed

crates/aptos/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ All notable changes to the Aptos CLI will be captured in this file. This project
44

55
# Unreleased
66
- When using `--bind-to 0.0.0.0`, clients created by the localnet will try to connect at 127.0.0.1, not 0.0.0.0.
7+
- Add `--docker-network` flag to allow specifying existing Docker network.
78

89
## [7.10.2]
910
- Fix backward compatibility issue of enum-based option module

crates/aptos/src/node/local_testnet/docker.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,20 @@ pub async fn pull_docker_image(image_name: &str) -> Result<()> {
111111
}
112112

113113
/// Create a network. If the network already exists, that's fine, just move on.
114+
/// Check if a Docker network exists.
115+
pub async fn network_exists(network_name: &str) -> Result<bool> {
116+
let docker = get_docker().await?;
117+
118+
// Try to inspect the network. If it exists, this will succeed.
119+
match docker.inspect_network::<&str>(network_name, None).await {
120+
Ok(_) => Ok(true),
121+
Err(BollardError::DockerResponseServerError {
122+
status_code: 404, ..
123+
}) => Ok(false),
124+
Err(err) => Err(err.into()),
125+
}
126+
}
127+
114128
pub async fn create_network(network_name: &str) -> Result<()> {
115129
let docker = get_docker().await?;
116130

crates/aptos/src/node/local_testnet/indexer_api.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use super::{
55
docker::{
66
delete_container, get_docker, pull_docker_image, setup_docker_logging,
7-
StopContainerShutdownStep, CONTAINER_NETWORK_NAME,
7+
StopContainerShutdownStep,
88
},
99
health_checker::HealthChecker,
1010
traits::{PostHealthyStep, ServiceManager, ShutdownStep},
@@ -70,6 +70,7 @@ pub struct IndexerApiManager {
7070
prerequisite_health_checkers: HashSet<HealthChecker>,
7171
test_dir: PathBuf,
7272
postgres_connection_string: String,
73+
docker_network: String,
7374
}
7475

7576
impl IndexerApiManager {
@@ -79,13 +80,15 @@ impl IndexerApiManager {
7980
test_dir: PathBuf,
8081
postgres_connection_string: String,
8182
) -> Result<Self> {
83+
let (docker_network, _) = args.get_docker_network();
8284
Ok(Self {
8385
indexer_api_port: args.indexer_api_args.indexer_api_port,
8486
existing_hasura_url: args.indexer_api_args.existing_hasura_url.clone(),
8587
skip_metadata_apply: args.indexer_api_args.skip_metadata_apply,
8688
prerequisite_health_checkers,
8789
test_dir,
8890
postgres_connection_string,
91+
docker_network: docker_network.to_string(),
8992
})
9093
}
9194

@@ -188,7 +191,7 @@ impl ServiceManager for IndexerApiManager {
188191
// in the Postgres pre_run steps.
189192
(
190193
self.postgres_connection_string,
191-
Some(CONTAINER_NETWORK_NAME.to_string()),
194+
Some(self.docker_network.clone()),
192195
)
193196
};
194197

crates/aptos/src/node/local_testnet/mod.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,26 @@ pub struct RunLocalnet {
105105
/// By default, tracing output goes to files. With this set, it goes to stdout.
106106
#[clap(long, hide = true)]
107107
log_to_stdout: bool,
108+
109+
/// If set, use an existing Docker network instead of creating a new one.
110+
///
111+
/// When specified, the CLI will verify that this network exists and use it for all
112+
/// Docker containers in the localnet. If not specified, a network named
113+
/// "aptos-local-testnet-network" will be created automatically.
114+
#[clap(long)]
115+
docker_network: Option<String>,
108116
}
109117

110118
impl RunLocalnet {
119+
/// Get the Docker network name to use for containers and whether it already exists.
120+
/// We assume if the user provided a network name that it exists.
121+
pub fn get_docker_network(&self) -> (&str, bool) {
122+
match self.docker_network {
123+
Some(ref network_name) => (network_name, true),
124+
None => (docker::CONTAINER_NETWORK_NAME, false),
125+
}
126+
}
127+
111128
/// Wait for many services to start up. This prints a message like "X is starting,
112129
/// please wait..." for each service and then "X is ready. Endpoint: <url>"
113130
/// when it's ready.
@@ -192,6 +209,29 @@ impl CliCommand<()> for RunLocalnet {
192209
setup_logging(None);
193210
}
194211

212+
if !self.indexer_api_args.with_indexer_api && self.docker_network.is_some() {
213+
return Err(CliError::UnexpectedError(
214+
"You cannot specify --docker-network without --with-indexer-api, Docker is only used when --with-indexer-api is set.".to_string(),
215+
));
216+
}
217+
218+
// If a custom Docker network is specified, verify that it exists. We only need
219+
// to do this if the indexer API is being run, otherwise we don't use Docker.
220+
if let Some(ref network_name) = self.docker_network {
221+
if !docker::network_exists(network_name).await.map_err(|e| {
222+
CliError::UnexpectedError(format!(
223+
"Failed to check if Docker network exists: {}",
224+
e
225+
))
226+
})? {
227+
return Err(CliError::UnexpectedError(format!(
228+
"Docker network '{}' does not exist. Please create it first or omit --docker-network to use the default network.",
229+
network_name
230+
)));
231+
}
232+
info!("Using existing Docker network: {}", network_name);
233+
}
234+
195235
// Based on the input and global config, get the test directory.
196236
let test_dir = get_derived_test_dir(&self.test_dir)?;
197237

crates/aptos/src/node/local_testnet/postgres.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use super::{
55
docker::{
66
create_network, create_volume, delete_container, delete_volume, get_docker,
7-
pull_docker_image, setup_docker_logging, StopContainerShutdownStep, CONTAINER_NETWORK_NAME,
7+
pull_docker_image, setup_docker_logging, StopContainerShutdownStep,
88
},
99
health_checker::HealthChecker,
1010
traits::{ServiceManager, ShutdownStep},
@@ -121,6 +121,8 @@ pub struct PostgresManager {
121121
args: PostgresArgs,
122122
test_dir: PathBuf,
123123
force_restart: bool,
124+
docker_network: String,
125+
docker_network_already_exists: bool,
124126
}
125127

126128
impl PostgresManager {
@@ -130,10 +132,13 @@ impl PostgresManager {
130132
{
131133
bail!("The postgres database cannot be named postgres if --use-host-postgres is set");
132134
}
135+
let (docker_network, docker_network_already_exists) = args.get_docker_network();
133136
Ok(Self {
134137
args: args.postgres_args.clone(),
135138
test_dir,
136139
force_restart: args.force_restart,
140+
docker_network: docker_network.to_string(),
141+
docker_network_already_exists,
137142
})
138143
}
139144

@@ -192,8 +197,11 @@ impl ServiceManager for PostgresManager {
192197
// `run_service`.
193198
pull_docker_image(POSTGRES_IMAGE).await?;
194199

195-
// Create a network for the containers to talk to each other.
196-
create_network(CONTAINER_NETWORK_NAME).await?;
200+
// Create a network for the containers to talk to each other if a
201+
// pre-existing network was not specified.
202+
if !self.docker_network_already_exists {
203+
create_network(&self.docker_network).await?;
204+
}
197205
}
198206

199207
Ok(())
@@ -243,7 +251,7 @@ impl ServiceManager for PostgresManager {
243251
// Bind the container to the network we created in the pre_run. This does
244252
// not prevent the binary in the container from exposing itself to the host
245253
// on 127.0.0.1. See more here: https://stackoverflow.com/a/77432636/3846032.
246-
network_mode: Some(CONTAINER_NETWORK_NAME.to_string()),
254+
network_mode: Some(self.docker_network.clone()),
247255
port_bindings: Some(hashmap! {
248256
POSTGRES_DEFAULT_PORT.to_string() => Some(vec![PortBinding {
249257
host_ip: Some("127.0.0.1".to_string()),

0 commit comments

Comments
 (0)