diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index cac1058779d..6ed81163731 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -73,7 +73,9 @@ jobs: cargo run --release -p linera-storage-service -- memory --endpoint $LINERA_STORAGE_SERVICE & - name: Wait for storage service to be ready run: | - until nc -z 127.0.0.1 1235; do sleep 1; done + STORAGE_HOST=$(echo "$LINERA_STORAGE_SERVICE" | cut -d: -f1) + STORAGE_PORT=$(echo "$LINERA_STORAGE_SERVICE" | cut -d: -f2) + until nc -z "$STORAGE_HOST" "$STORAGE_PORT"; do sleep 1; done - name: Build binaries run: | cargo build --features storage-service --bin linera-server --bin linera-proxy --bin linera @@ -87,14 +89,53 @@ jobs: mkdir /tmp/linera-faucet cargo run --bin linera -- resource-control-policy --http-request-timeout-ms 1000 cargo run --bin linera -- resource-control-policy --http-request-timeout-ms 500 - cargo run --bin linera -- faucet --storage-path /tmp/linera-faucet/faucet_storage.sqlite --amount 1000 --port 8079 & + FAUCET_PORT=$(echo "$LINERA_FAUCET_URL" | cut -d: -f3) + cargo run --bin linera -- faucet --storage-path /tmp/linera-faucet/faucet_storage.sqlite --amount 1000 --port $FAUCET_PORT & - name: Wait for faucet to be ready run: | - until curl -s http://localhost:8079 >/dev/null; do sleep 1; done + until curl -s $LINERA_FAUCET_URL >/dev/null; do sleep 1; done - name: Run the remote-net tests run: | cargo test -p linera-service remote_net_grpc --features remote-net + remote-kubernetes-net-test: + needs: changed-files + if: needs.changed-files.outputs.should-run == 'true' + runs-on: ubuntu-latest-16-cores + timeout-minutes: 90 + + steps: + - uses: actions/checkout@v4 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + - name: Install Protoc + uses: arduino/setup-protoc@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Install helmfile + run: | + # Install specific version to match local development environment + HELMFILE_VERSION="v0.168.0" + curl -fsSL -o helmfile.tar.gz "https://github.com/helmfile/helmfile/releases/download/${HELMFILE_VERSION}/helmfile_${HELMFILE_VERSION#v}_linux_amd64.tar.gz" + tar -xzf helmfile.tar.gz helmfile + sudo mv helmfile /usr/local/bin/ + rm helmfile.tar.gz + # Verify installation + which helmfile && helmfile --version + - name: Build binaries + run: | + cargo build --features storage-service --bin linera-server --bin linera-proxy --bin linera + - name: Run the validators with Kubernetes + run: | + mkdir /tmp/local-linera-net + FAUCET_PORT=$(echo "$LINERA_FAUCET_URL" | cut -d: -f3) + cargo run --bin linera --features kubernetes -- net up --kubernetes --policy-config testnet --path /tmp/local-linera-net --validators 1 --shards 2 --with-faucet --faucet-port $FAUCET_PORT --faucet-amount 1000 & + - name: Wait for faucet to be ready + run: | + until curl -s $LINERA_FAUCET_URL >/dev/null; do sleep 1; done + - name: Run the Kubernetes tests + run: | + cargo test -p linera-service remote_net_grpc --features remote-net + execution-wasmtime-test: needs: changed-files if: needs.changed-files.outputs.should-run == 'true' @@ -226,7 +267,9 @@ jobs: cargo run --release -p linera-storage-service -- memory --endpoint $LINERA_STORAGE_SERVICE & - name: Wait for storage service to be ready run: | - until nc -z 127.0.0.1 1235; do sleep 1; done + STORAGE_HOST=$(echo "$LINERA_STORAGE_SERVICE" | cut -d: -f1) + STORAGE_PORT=$(echo "$LINERA_STORAGE_SERVICE" | cut -d: -f2) + until nc -z "$STORAGE_HOST" "$STORAGE_PORT"; do sleep 1; done - name: Run the benchmark test run: | cargo build --locked -p linera-service --bin linera-benchmark --features storage-service @@ -286,7 +329,9 @@ jobs: cargo run --release -p linera-storage-service -- memory --endpoint $LINERA_STORAGE_SERVICE & - name: Wait for storage service to be ready run: | - until nc -z 127.0.0.1 1235; do sleep 1; done + STORAGE_HOST=$(echo "$LINERA_STORAGE_SERVICE" | cut -d: -f1) + STORAGE_PORT=$(echo "$LINERA_STORAGE_SERVICE" | cut -d: -f2) + until nc -z "$STORAGE_HOST" "$STORAGE_PORT"; do sleep 1; done - name: Run Ethereum tests run: | cargo test -p linera-ethereum --features ethereum @@ -316,7 +361,9 @@ jobs: cargo run --release -p linera-storage-service -- memory --endpoint $LINERA_STORAGE_SERVICE & - name: Wait for storage service to be ready run: | - until nc -z 127.0.0.1 1235; do sleep 1; done + STORAGE_HOST=$(echo "$LINERA_STORAGE_SERVICE" | cut -d: -f1) + STORAGE_PORT=$(echo "$LINERA_STORAGE_SERVICE" | cut -d: -f2) + until nc -z "$STORAGE_HOST" "$STORAGE_PORT"; do sleep 1; done - name: Run the storage-service tests run: | cargo test --features storage-service -- storage_service --nocapture diff --git a/linera-service/src/cli/main.rs b/linera-service/src/cli/main.rs index b8f568c6719..bcc8cd4bbe5 100644 --- a/linera-service/src/cli/main.rs +++ b/linera-service/src/cli/main.rs @@ -2271,6 +2271,7 @@ async fn run(options: &ClientOptions) -> Result { indexer_image_name, explorer_image_name, dual_store, + path, .. } => { net_up_utils::handle_net_up_kubernetes( @@ -2294,6 +2295,7 @@ async fn run(options: &ClientOptions) -> Result { indexer_image_name.clone(), explorer_image_name.clone(), *dual_store, + path, ) .boxed() .await?; diff --git a/linera-service/src/cli/net_up_utils.rs b/linera-service/src/cli/net_up_utils.rs index 7de14803516..f2667c7e3ee 100644 --- a/linera-service/src/cli/net_up_utils.rs +++ b/linera-service/src/cli/net_up_utils.rs @@ -130,6 +130,7 @@ pub async fn handle_net_up_kubernetes( indexer_image_name: String, explorer_image_name: String, dual_store: bool, + path: &Option, ) -> anyhow::Result<()> { assert!( num_initial_validators >= 1, @@ -180,6 +181,7 @@ pub async fn handle_net_up_kubernetes( indexer_image_name, explorer_image_name, dual_store, + path_provider: PathProvider::from_path_option(path)?, }; let (mut net, client) = config.instantiate().await?; let faucet_service = print_messages_and_create_faucet( @@ -338,18 +340,23 @@ async fn print_messages_and_create_faucet( // Run the faucet, let faucet_service = if with_faucet { - let faucet_chain_idx = faucet_chain.unwrap_or(0); - assert!( - num_other_initial_chains > faucet_chain_idx, - "num_other_initial_chains must be strictly greater than the faucet chain index if \ + let faucet_chain = if let Some(faucet_chain_idx) = faucet_chain { + assert!( + num_other_initial_chains > faucet_chain_idx, + "num_other_initial_chains must be strictly greater than the faucet chain index if \ with_faucet is true" - ); - // This picks a lexicographically faucet_chain_idx-th non-admin chain. - let faucet_chain = chains - .into_iter() - .filter(|chain_id| *chain_id != wallet.genesis_admin_chain()) - .nth(faucet_chain_idx as usize) - .unwrap(); // we checked that there are enough chains above, so this should be safe + ); + // This picks a lexicographically faucet_chain_idx-th non-admin chain. + Some( + chains + .into_iter() + .filter(|chain_id| *chain_id != wallet.genesis_admin_chain()) + .nth(faucet_chain_idx as usize) + .unwrap(), + ) // we checked that there are enough chains above, so this should be safe + } else { + None + }; let service = client .run_faucet(Some(faucet_port.into()), faucet_chain, faucet_amount) .await?; diff --git a/linera-service/src/cli_wrappers/local_kubernetes_net.rs b/linera-service/src/cli_wrappers/local_kubernetes_net.rs index eb4fc29240c..16d58f6961a 100644 --- a/linera-service/src/cli_wrappers/local_kubernetes_net.rs +++ b/linera-service/src/cli_wrappers/local_kubernetes_net.rs @@ -13,7 +13,6 @@ use linera_base::{ data_types::Amount, }; use linera_client::client_options::ResourceControlPolicyConfig; -use tempfile::{tempdir, TempDir}; use tokio::{process::Command, task::JoinSet}; #[cfg(with_testing)] use {linera_base::command::current_binary_parent, tokio::sync::OnceCell}; @@ -73,6 +72,7 @@ pub struct LocalKubernetesNetConfig { pub indexer_image_name: String, pub explorer_image_name: String, pub dual_store: bool, + pub path_provider: PathProvider, } /// A wrapper of [`LocalKubernetesNetConfig`] to create a shared local Kubernetes network @@ -86,7 +86,6 @@ pub struct LocalKubernetesNet { network: Network, testing_prng_seed: Option, next_client_id: usize, - tmp_dir: Arc, binaries: BuildArg, no_build: bool, docker_image_name: String, @@ -100,6 +99,7 @@ pub struct LocalKubernetesNet { indexer_image_name: String, explorer_image_name: String, dual_store: bool, + path_provider: PathProvider, } #[cfg(with_testing)] @@ -139,6 +139,8 @@ impl SharedLocalKubernetesNetTestingConfig { indexer_image_name: String::from("linera-indexer:latest"), explorer_image_name: String::from("linera-explorer:latest"), dual_store: false, + path_provider: PathProvider::create_temporary_directory() + .expect("Creating temporary directory should not fail"), }) } } @@ -176,6 +178,7 @@ impl LineraNetConfig for LocalKubernetesNetConfig { self.indexer_image_name, self.explorer_image_name, self.dual_store, + self.path_provider, )?; let client = net.make_client().await; @@ -292,11 +295,8 @@ impl LineraNet for LocalKubernetesNet { } async fn make_client(&mut self) -> ClientWrapper { - let path_provider = PathProvider::TemporaryDirectory { - tmp_dir: self.tmp_dir.clone(), - }; let client = ClientWrapper::new( - path_provider, + self.path_provider.clone(), self.network, self.testing_prng_seed, self.next_client_id, @@ -360,12 +360,12 @@ impl LocalKubernetesNet { indexer_image_name: String, explorer_image_name: String, dual_store: bool, + path_provider: PathProvider, ) -> Result { Ok(Self { network, testing_prng_seed, next_client_id: 0, - tmp_dir: Arc::new(tempdir()?), binaries, no_build, docker_image_name, @@ -379,22 +379,23 @@ impl LocalKubernetesNet { indexer_image_name, explorer_image_name, dual_store, + path_provider, }) } async fn command_for_binary(&self, name: &'static str) -> Result { let path = resolve_binary(name, env!("CARGO_PKG_NAME")).await?; let mut command = Command::new(path); - command.current_dir(self.tmp_dir.path()); + command.current_dir(self.path_provider.path()); Ok(command) } fn configuration_string(&self, validator_number: usize) -> Result { let path = self - .tmp_dir + .path_provider .path() .join(format!("validator_{validator_number}.toml")); - let public_port = 19100; + let public_port = 19100 + validator_number; let private_port = 20100; let metrics_port = 21100; let protocol = self.network.toml(); @@ -535,12 +536,12 @@ impl LocalKubernetesNet { .join("linera-validator") .join("working"); fs_err::copy( - self.tmp_dir.path().join("genesis.json"), + self.path_provider.path().join("genesis.json"), base_dir.join("genesis.json"), )?; let kubectl_instance_clone = self.kubectl_instance.clone(); - let tmp_dir_path_clone = self.tmp_dir.path().to_path_buf(); + let path_provider_path_clone = self.path_provider.path().to_path_buf(); let num_proxies = self.num_proxies; let num_shards = self.num_shards; @@ -550,7 +551,7 @@ impl LocalKubernetesNet { let github_root = github_root.clone(); let kubectl_instance = kubectl_instance_clone.clone(); - let tmp_dir_path = tmp_dir_path_clone.clone(); + let path_provider_path = path_provider_path_clone.clone(); let docker_image_name = docker_image_name.clone(); let indexer_image_name = indexer_image_name.clone(); @@ -565,7 +566,7 @@ impl LocalKubernetesNet { let server_config_filename = format!("server_{}.json", validator_number); fs_err::copy( - tmp_dir_path.join(&server_config_filename), + path_provider_path.join(&server_config_filename), base_dir.join(&server_config_filename), )?; diff --git a/linera-service/src/cli_wrappers/wallet.rs b/linera-service/src/cli_wrappers/wallet.rs index 497ca11ca6f..d5556b48acd 100644 --- a/linera-service/src/cli_wrappers/wallet.rs +++ b/linera-service/src/cli_wrappers/wallet.rs @@ -540,7 +540,7 @@ impl ClientWrapper { pub async fn run_faucet( &self, port: impl Into>, - chain_id: ChainId, + chain_id: Option, amount: Amount, ) -> Result { let port = port.into().unwrap_or(8080); @@ -548,16 +548,18 @@ impl ClientWrapper { .context("Failed to create temporary directory for faucet storage")?; let storage_path = temp_dir.path().join("faucet_storage.sqlite"); let mut command = self.command().await?; - let child = command + let command = command .arg("faucet") - .arg(chain_id.to_string()) .args(["--port".to_string(), port.to_string()]) .args(["--amount".to_string(), amount.to_string()]) .args([ "--storage-path".to_string(), storage_path.to_string_lossy().to_string(), - ]) - .spawn_into()?; + ]); + if let Some(chain_id) = chain_id { + command.arg(chain_id.to_string()); + } + let child = command.spawn_into()?; let client = reqwest_client(); for i in 0..10 { linera_base::time::timer::sleep(Duration::from_secs(i)).await; diff --git a/linera-service/tests/linera_net_tests.rs b/linera-service/tests/linera_net_tests.rs index e02a7c747d6..0949ab4de53 100644 --- a/linera-service/tests/linera_net_tests.rs +++ b/linera-service/tests/linera_net_tests.rs @@ -4167,7 +4167,7 @@ async fn test_end_to_end_faucet(config: impl LineraNetConfig) -> Result<()> { let owner2 = client2.keygen().await?; let mut faucet_service = client1 - .run_faucet(None, chain1, Amount::from_tokens(2)) + .run_faucet(None, Some(chain1), Amount::from_tokens(2)) .await?; let faucet = faucet_service.instance(); let chain2 = faucet.claim(&owner2).await?.id(); @@ -4252,7 +4252,7 @@ async fn test_end_to_end_faucet_with_long_chains(config: impl LineraNetConfig) - let new_chain_init_balance = Amount::ONE; let mut faucet_service = faucet_client - .run_faucet(None, faucet_chain, new_chain_init_balance) + .run_faucet(None, Some(faucet_chain), new_chain_init_balance) .await?; let faucet = faucet_service.instance(); @@ -4323,7 +4323,7 @@ async fn test_end_to_end_faucet_batch_processing(config: impl LineraNetConfig) - // Start faucet with small batch size for testing let mut faucet_service = client1 - .run_faucet(None, chain1, Amount::from_tokens(2)) + .run_faucet(None, Some(chain1), Amount::from_tokens(2)) .await?; let faucet = faucet_service.instance(); @@ -4412,7 +4412,7 @@ async fn test_end_to_end_fungible_client_benchmark(config: impl LineraNetConfig) let chain1 = client1.load_wallet()?.default_chain().unwrap(); - let mut faucet_service = client1.run_faucet(None, chain1, Amount::ONE).await?; + let mut faucet_service = client1.run_faucet(None, Some(chain1), Amount::ONE).await?; let faucet = faucet_service.instance(); let path = diff --git a/linera-service/tests/local_net_tests.rs b/linera-service/tests/local_net_tests.rs index c6751feb237..371622bf8f3 100644 --- a/linera-service/tests/local_net_tests.rs +++ b/linera-service/tests/local_net_tests.rs @@ -67,7 +67,7 @@ async fn test_end_to_end_reconfiguration(config: LocalNetConfig) -> Result<()> { .await?; let mut faucet_service = faucet_client - .run_faucet(None, faucet_chain, Amount::from_tokens(2)) + .run_faucet(None, Some(faucet_chain), Amount::from_tokens(2)) .await?; faucet_service.ensure_is_running()?; @@ -284,7 +284,7 @@ async fn test_end_to_end_receipt_of_old_create_committee_messages( if matches!(network, Network::Grpc) { let mut faucet_service = faucet_client - .run_faucet(None, faucet_chain, Amount::from_tokens(2)) + .run_faucet(None, Some(faucet_chain), Amount::from_tokens(2)) .await?; faucet_service.ensure_is_running()?; @@ -327,7 +327,7 @@ async fn test_end_to_end_receipt_of_old_create_committee_messages( faucet_client.process_inbox(faucet_chain).await?; let mut faucet_service = faucet_client - .run_faucet(None, faucet_chain, Amount::from_tokens(2)) + .run_faucet(None, Some(faucet_chain), Amount::from_tokens(2)) .await?; faucet_service.ensure_is_running()?; @@ -381,7 +381,7 @@ async fn test_end_to_end_receipt_of_old_remove_committee_messages( if matches!(network, Network::Grpc) { let mut faucet_service = faucet_client - .run_faucet(None, faucet_chain, Amount::from_tokens(2)) + .run_faucet(None, Some(faucet_chain), Amount::from_tokens(2)) .await?; faucet_service.ensure_is_running()?; @@ -427,7 +427,7 @@ async fn test_end_to_end_receipt_of_old_remove_committee_messages( if matches!(network, Network::Grpc) { let mut faucet_service = faucet_client - .run_faucet(None, faucet_chain, Amount::from_tokens(2)) + .run_faucet(None, Some(faucet_chain), Amount::from_tokens(2)) .await?; faucet_service.ensure_is_running()?; @@ -472,7 +472,7 @@ async fn test_end_to_end_receipt_of_old_remove_committee_messages( faucet_client.process_inbox(faucet_chain).await?; let mut faucet_service = faucet_client - .run_faucet(None, faucet_chain, Amount::from_tokens(2)) + .run_faucet(None, Some(faucet_chain), Amount::from_tokens(2)) .await?; faucet_service.ensure_is_running()?;