Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CLI] Restructure local testnet code #10252

Merged
merged 1 commit into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ dirs = "5.0.1"
ed25519-dalek = { version = "1.0.1", features = ["std", "serde"] }
ed25519-dalek-bip32 = "0.2.0"
either = "1.6.1"
enum_dispatch = "0.3.8"
enum_dispatch = "0.3.12"
env_logger = "0.10.0"
erased-serde = "0.3.13"
event-listener = "2.5.3"
Expand Down
2 changes: 2 additions & 0 deletions crates/aptos/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ dirs = { workspace = true }
futures = { workspace = true }
hex = { workspace = true }
itertools = { workspace = true }
maplit = { workspace = true }
move-binary-format = { workspace = true }
move-bytecode-source-map = { workspace = true }
move-cli = { workspace = true }
Expand Down Expand Up @@ -86,6 +87,7 @@ thiserror = { workspace = true }
tokio = { workspace = true }
toml = { workspace = true }
tonic = { workspace = true }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
walkdir = { workspace = true }

Expand Down
7 changes: 4 additions & 3 deletions crates/aptos/e2e/cases/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ def test_account_create_and_transfer(run_helper: RunHelper, test_name=None):
raise TestError(
f"Account {OTHER_ACCOUNT_ONE.account_address} has balance {balance}, expected 0"
)

transfer_amount = 1000

run_helper.run_command(
test_name,
[
Expand All @@ -87,7 +87,7 @@ def test_account_create_and_transfer(run_helper: RunHelper, test_name=None):
raise TestError(
f"Account {OTHER_ACCOUNT_ONE.account_address} has balance {balance}, expected {transfer_amount}"
)


@test_case
def test_account_list(run_helper: RunHelper, test_name=None):
Expand Down Expand Up @@ -116,6 +116,7 @@ def test_account_list(run_helper: RunHelper, test_name=None):
"Cannot find the account in the account list after account creation"
)


@test_case
def test_account_lookup_address(run_helper: RunHelper, test_name=None):
# Create the new account.
Expand Down
48 changes: 27 additions & 21 deletions crates/aptos/e2e/local_testnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

# Run a local testnet in a docker container. We choose to detach here and we'll
# stop running it later using the container name.
def run_node(network: Network, image_repo_with_project: str):
def run_node(network: Network, image_repo_with_project: str, pull=True):
image_name = build_image_name(image_repo_with_project, network)
container_name = f"aptos-tools-{network}"
LOG.info(f"Trying to run aptos CLI local testnet from image: {image_name}")
Expand Down Expand Up @@ -43,28 +43,34 @@ def run_node(network: Network, image_repo_with_project: str):
if LOG.getEffectiveLevel() > 10:
kwargs = {**kwargs, **{"stdout": subprocess.PIPE, "stderr": subprocess.PIPE}}

args = [
"docker",
"run",
]

if pull:
args += ["--pull", "always"]

args += [
"--detach",
"--name",
container_name,
"-p",
f"{NODE_PORT}:{NODE_PORT}",
"-p",
f"{METRICS_PORT}:{METRICS_PORT}",
"-p",
f"{FAUCET_PORT}:{FAUCET_PORT}",
image_name,
"aptos",
"node",
"run-local-testnet",
"--with-faucet",
]

# Run the container.
subprocess.run(
[
"docker",
"run",
"--pull",
"always",
"--detach",
"--name",
container_name,
"-p",
f"{NODE_PORT}:{NODE_PORT}",
"-p",
f"{METRICS_PORT}:{METRICS_PORT}",
"-p",
f"{FAUCET_PORT}:{FAUCET_PORT}",
image_name,
"aptos",
"node",
"run-local-testnet",
"--with-faucet",
],
args,
**kwargs,
)

Expand Down
9 changes: 8 additions & 1 deletion crates/aptos/e2e/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ def parse_args():
default="/tmp/aptos-cli-tests",
help="Where we'll run CLI commands from (in the host system). Default: %(default)s",
)
parser.add_argument(
"--no-pull-always",
action="store_true",
help='If set, do not set "--pull always" when running the local testnet. Necessary for using local images.',
)
args = parser.parse_args()
return args

Expand Down Expand Up @@ -189,7 +194,9 @@ def main():
pathlib.Path(args.working_directory).mkdir(parents=True, exist_ok=True)

# Run a node + faucet and wait for them to start up.
container_name = run_node(args.base_network, args.image_repo_with_project)
container_name = run_node(
args.base_network, args.image_repo_with_project, not args.no_pull_always
)

# We run these in a try finally so that if something goes wrong, such as the
# local testnet not starting up correctly or some unexpected error in the
Expand Down
87 changes: 87 additions & 0 deletions crates/aptos/src/node/local_testnet/faucet.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

use super::{health_checker::HealthChecker, traits::ServiceManager, RunLocalTestnet};
use anyhow::Result;
use aptos_faucet_core::server::{FunderKeyEnum, RunConfig};
use async_trait::async_trait;
use clap::Parser;
use maplit::hashset;
use reqwest::Url;
use std::{collections::HashSet, path::PathBuf};

/// Args related to running a faucet in the local testnet.
#[derive(Debug, Parser)]
pub struct FaucetArgs {
/// Do not run a faucet alongside the node.
///
/// Running a faucet alongside the node allows you to create and fund accounts
/// for testing.
#[clap(long)]
pub no_faucet: bool,

/// This does nothing, we already run a faucet by default. We only keep this here
/// for backwards compatibility with tests. We will remove this once the commit
/// that added --no-faucet makes its way to the testnet branch.
#[clap(long, hide = true)]
pub with_faucet: bool,

/// Port to run the faucet on.
///
/// When running, you'll be able to use the faucet at `http://127.0.0.1:<port>/mint` e.g.
/// `http//127.0.0.1:8081/mint`
#[clap(long, default_value_t = 8081)]
pub faucet_port: u16,

/// Disable the delegation of faucet minting to a dedicated account.
#[clap(long)]
pub do_not_delegate: bool,
}

#[derive(Clone, Debug)]
pub struct FaucetManager {
config: RunConfig,
prerequisite_health_checkers: HashSet<HealthChecker>,
}

impl FaucetManager {
pub fn new(
args: &RunLocalTestnet,
prerequisite_health_checkers: HashSet<HealthChecker>,
test_dir: PathBuf,
node_api_url: Url,
) -> Result<Self> {
Ok(Self {
config: RunConfig::build_for_cli(
node_api_url.clone(),
args.faucet_args.faucet_port,
FunderKeyEnum::KeyFile(test_dir.join("mint.key")),
args.faucet_args.do_not_delegate,
None,
),
prerequisite_health_checkers,
})
}
}

#[async_trait]
impl ServiceManager for FaucetManager {
fn get_name(&self) -> String {
"Faucet".to_string()
}

fn get_healthchecks(&self) -> HashSet<HealthChecker> {
hashset! {HealthChecker::http_checker_from_port(
self.config.server_config.listen_port,
self.get_name(),
)}
}

fn get_prerequisite_health_checkers(&self) -> HashSet<&HealthChecker> {
self.prerequisite_health_checkers.iter().collect()
}

async fn run_service(self: Box<Self>) -> Result<()> {
self.config.run().await
}
}
28 changes: 23 additions & 5 deletions crates/aptos/src/node/local_testnet/health_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ const MAX_WAIT_S: u64 = 35;
const WAIT_INTERVAL_MS: u64 = 150;

/// This provides a single place to define a variety of different healthchecks.
#[derive(Clone, Debug, Serialize)]
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize)]
pub enum HealthChecker {
/// Check that an HTTP API is up. The second param is the name of the HTTP service.
Http(Url, &'static str),
Http(Url, String),
/// Check that the node API is up. This is just a specific case of Http for extra
/// guarantees around liveliness.
NodeApi(Url),
Expand Down Expand Up @@ -100,6 +100,14 @@ impl HealthChecker {
HealthChecker::DataServiceGrpc(url) => url.as_str(),
}
}

/// Given a port, make an instance of HealthChecker::Http targeting 127.0.0.1.
pub fn http_checker_from_port(port: u16, name: String) -> Self {
Self::Http(
Url::parse(&format!("http://127.0.0.1:{}", port,)).unwrap(),
name,
)
}
}

impl std::fmt::Display for HealthChecker {
Expand All @@ -123,15 +131,25 @@ where
let start = Instant::now();
let mut started_successfully = false;

let mut last_error_message = None;
while start.elapsed() < max_wait {
if check_fn().await.is_ok() {
started_successfully = true;
break;
match check_fn().await {
Ok(_) => {
started_successfully = true;
break;
},
Err(err) => {
last_error_message = Some(format!("{:#}", err));
},
}
tokio::time::sleep(wait_interval).await
}

if !started_successfully {
let error_message = match last_error_message {
Some(last_error_message) => format!("{}: {}", error_message, last_error_message),
None => error_message,
};
return Err(CliError::UnexpectedError(error_message));
}

Expand Down
Loading
Loading