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] Support Docker socket in home directory for local testnet #10654

Merged
merged 1 commit into from
Oct 25, 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
10 changes: 5 additions & 5 deletions Cargo.lock

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

4 changes: 4 additions & 0 deletions crates/aptos/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@
All notable changes to the Aptos CLI will be captured in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and the format set out by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## Unreleased

## [2.3.0] - 2023/10/25
### Added
- Added `--node-api-key`. This lets you set an API key for the purpose of not being ratelimited.

### Updated
- Made the local testnet exit more quickly if a service fails to start.
- Updated processor code from https://github.com/aptos-labs/aptos-indexer-processors for the local testnet to bcba94c26c8a6372056d2b69ce411c5719f98965.

### Fixed
- Fixed an infrequent bug that caused startup failures for the local testnet with `--force-restart` + `--with-indexer-api` by using a Docker volume rather than a bind mount for the postgres storage.
- Fixed an issue where the CLI could not find the Docker socket with some Docker Desktop configurations.

## [2.2.2] - 2023/10/16
### Updated
Expand Down
6 changes: 3 additions & 3 deletions crates/aptos/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "aptos"
description = "Aptos tool for management of nodes and interacting with the blockchain"
version = "2.2.2"
version = "2.3.0"

# Workspace inherited keys
authors = { workspace = true }
Expand Down Expand Up @@ -79,15 +79,15 @@ move-unit-test = { workspace = true, features = [ "debugging" ] }
move-vm-runtime = { workspace = true, features = [ "testing" ] }
once_cell = { workspace = true }
poem = { workspace = true }
processor = { git = "https://github.com/aptos-labs/aptos-indexer-processors.git", rev = "a9e42dc9d80a69e9d02cd5f19c39e0f3f5cfbeb0" }
processor = { git = "https://github.com/aptos-labs/aptos-indexer-processors.git", rev = "bcba94c26c8a6372056d2b69ce411c5719f98965" }
rand = { workspace = true }
regex = { workspace = true }
reqwest = { workspace = true }
self_update = { version = "0.38.0", features = ["archive-zip", "compression-zip-deflate"] }
serde = { workspace = true }
serde_json = { workspace = true }
serde_yaml = { workspace = true }
server-framework = { git = "https://github.com/aptos-labs/aptos-indexer-processors.git", rev = "a9e42dc9d80a69e9d02cd5f19c39e0f3f5cfbeb0" }
server-framework = { git = "https://github.com/aptos-labs/aptos-indexer-processors.git", rev = "bcba94c26c8a6372056d2b69ce411c5719f98965" }
tempfile = { workspace = true }
termcolor = { workspace = true }
thiserror = { workspace = true }
Expand Down
80 changes: 53 additions & 27 deletions crates/aptos/src/node/local_testnet/docker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,60 @@ use bollard::{
container::{RemoveContainerOptions, StopContainerOptions},
image::CreateImageOptions,
volume::{CreateVolumeOptions, RemoveVolumeOptions},
Docker,
Docker, API_DEFAULT_VERSION,
};
use futures::TryStreamExt;
use std::{fs::create_dir_all, path::Path};
use tracing::{info, warn};
use version_compare::Version;

pub fn get_docker() -> Result<Docker> {
let docker = Docker::connect_with_local_defaults()
.context("Docker is not available, confirm it is installed and running. On Linux you may need to use sudo.")?;
Ok(docker)
}

pub async fn confirm_docker_available() -> Result<()> {
let docker = get_docker()?;
let info = docker
.info()
.await
.context("Docker is not available, confirm it is installed and running. On Linux you may need to use sudo.")?;

info!("Docker Info: {:?}", info);

let version = docker
.version()
.await
.context("Failed to get Docker version")?;
const ERROR_MESSAGE: &str = "Docker is not available, confirm it is installed and running. See https://aptos.dev/guides/local-development-network#faq for assistance.";

info!("Docker Version: {:?}", version);
/// This function returns a Docker client. Before returning, it confirms that it can
/// actually query the API and checks that the API version is sufficient. It first
/// tries to connect at the default socket location and if that fails, it tries to find
/// a socket in the user's home directory. On Windows NT it doesn't try that since
/// there no second location, there is just the one named pipe.
pub async fn get_docker() -> Result<Docker> {
let docker = Docker::connect_with_local_defaults()
.context(format!("{} (init_default)", ERROR_MESSAGE))?;

// We have to specify the type because the compiler can't figure out the error
// in the case where the system is Unix.
let out: Result<(Docker, bollard::system::Version), bollard::errors::Error> =
match docker.version().await {
Ok(version) => Ok((docker, version)),
Err(err) => {
warn!(
"Received this error trying to use default Docker socket location: {:#}",
err
);
// Look for the socket in ~/.docker/run
// We don't have to do this if this issue gets addressed:
// https://github.com/fussybeaver/bollard/issues/345
#[cfg(unix)]
{
let path = dirs::home_dir()
.context(format!("{} (home_dir)", ERROR_MESSAGE))?
.join(".docker")
.join("run")
.join("docker.sock");
info!("Looking for Docker socket at {}", path.display());
let path = path.to_str().context(format!("{} (path)", ERROR_MESSAGE))?;
let docker = Docker::connect_with_socket(path, 120, API_DEFAULT_VERSION)
.context(format!("{} (init_home)", ERROR_MESSAGE))?;
let version = docker
.version()
.await
.context(format!("{} (version_home)", ERROR_MESSAGE))?;
Ok((docker, version))
}
// Just return the original error.
#[cfg(not(unix))]
Err(err)
},
};
let (docker, version) = out?;

// Try to warn the user about their Docker version being too old. We don't error
// out if the version is too old in case we're wrong about the minimum version
Expand Down Expand Up @@ -68,7 +94,7 @@ pub async fn confirm_docker_available() -> Result<()> {
},
}

Ok(())
Ok(docker)
}

/// Delete a container. If the container doesn't exist, that's fine, just move on.
Expand All @@ -78,7 +104,7 @@ pub async fn delete_container(container_name: &str) -> Result<()> {
container_name
);

let docker = get_docker()?;
let docker = get_docker().await?;

let options = Some(RemoveContainerOptions {
force: true,
Expand Down Expand Up @@ -106,7 +132,7 @@ pub async fn stop_container(container_name: &str) -> Result<()> {
container_name
);

let docker = get_docker()?;
let docker = get_docker().await?;

let options = Some(StopContainerOptions {
// Timeout in seconds before we kill the container.
Expand All @@ -130,7 +156,7 @@ pub async fn stop_container(container_name: &str) -> Result<()> {
pub async fn pull_docker_image(image_name: &str) -> Result<()> {
info!("Checking if we have to pull docker image {}", image_name);

let docker = get_docker()?;
let docker = get_docker().await?;

let options = Some(CreateImageOptions {
from_image: image_name,
Expand Down Expand Up @@ -164,7 +190,7 @@ pub async fn pull_docker_image(image_name: &str) -> Result<()> {
}

pub async fn create_volume(volume_name: &str) -> Result<()> {
let docker = get_docker()?;
let docker = get_docker().await?;

info!("Creating volume {}", volume_name);

Expand All @@ -180,7 +206,7 @@ pub async fn create_volume(volume_name: &str) -> Result<()> {
}

pub async fn delete_volume(volume_name: &str) -> Result<()> {
let docker = get_docker()?;
let docker = get_docker().await?;

info!("Removing volume {}", volume_name);

Expand Down
8 changes: 4 additions & 4 deletions crates/aptos/src/node/local_testnet/indexer_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

use super::{
docker::{
confirm_docker_available, delete_container, get_docker, pull_docker_image,
setup_docker_logging, StopContainerShutdownStep,
delete_container, get_docker, pull_docker_image, setup_docker_logging,
StopContainerShutdownStep,
},
health_checker::HealthChecker,
traits::{PostHealthyStep, ServiceManager, ShutdownStep},
Expand Down Expand Up @@ -89,7 +89,7 @@ impl ServiceManager for IndexerApiManager {

async fn pre_run(&self) -> Result<()> {
// Confirm Docker is available.
confirm_docker_available().await?;
get_docker().await?;

// Delete any existing indexer API container we find.
delete_container(INDEXER_API_CONTAINER_NAME).await?;
Expand Down Expand Up @@ -139,7 +139,7 @@ impl ServiceManager for IndexerApiManager {
..Default::default()
};

let docker = get_docker()?;
let docker = get_docker().await?;

// When using Docker Desktop you can and indeed must use the magic hostname
// host.docker.internal in order to access localhost on the host system from
Expand Down
8 changes: 4 additions & 4 deletions crates/aptos/src/node/local_testnet/postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

use super::{
docker::{
confirm_docker_available, create_volume, delete_container, delete_volume, get_docker,
pull_docker_image, setup_docker_logging, StopContainerShutdownStep,
create_volume, delete_container, delete_volume, get_docker, pull_docker_image,
setup_docker_logging, StopContainerShutdownStep,
},
health_checker::HealthChecker,
traits::{ServiceManager, ShutdownStep},
Expand Down Expand Up @@ -163,7 +163,7 @@ impl ServiceManager for PostgresManager {
}
} else {
// Confirm Docker is available.
confirm_docker_available().await?;
get_docker().await?;

// Kill any existing container we find.
delete_container(POSTGRES_CONTAINER_NAME).await?;
Expand Down Expand Up @@ -195,7 +195,7 @@ impl ServiceManager for PostgresManager {
// Let the user know where to go to see logs for postgres.
setup_docker_logging(&self.test_dir, "postgres", POSTGRES_CONTAINER_NAME)?;

let docker = get_docker()?;
let docker = get_docker().await?;

// If we're starting afresh, delete any existing volume.
if self.force_restart {
Expand Down
8 changes: 5 additions & 3 deletions developer-docs-site/docs/guides/local-development-network.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,9 @@ Make sure the socket for connecting to Docker is present on your machine in the
/var/run/docker.sock
```

If it doesn't, you can find where it is like this:
If it doesn't, open Docker Desktop and enable `Settings -> Advanced -> Allow the default Docker socket to be used`.

Alternatively, you can find where it is like this:
```
$ docker context inspect | grep Host
"Host": "unix:///Users/dport/.docker/run/docker.sock",
Expand All @@ -247,7 +249,7 @@ Alternatively, run the CLI like this to tell it where the socket is:
DEFAULT_SOCKET=/Users/dport/.docker/run/docker.sock aptos node run-local-testnet --with-indexer-api
```

Note: If you're on Mac or Windows, we recommend you use Docker Desktop rather than installing Docker via a package manager (e.g. Homebrew or Choco).
Note: As mentioned above, if you're on Mac or Windows, we recommend you use Docker Desktop rather than installing Docker via a package manager (e.g. Homebrew or Choco).

### The local network seems to hang on startup
If the CLI seems to sit there and do nothing when you are using `--with-indexer-api`, consider quitting and restarting Docker. Sometimes Docker gets in a bad state. Note that Docker is only required if you are using `--with-indexer-api`.
Expand All @@ -264,7 +266,7 @@ When running the CLI interactively, you can see if the network is alive by waiti
Setup is complete, you can now use the local testnet!
```

If you writing a script and would like to wait for the local network to come up, you can make a GET request to `http://127.0.0.1:8070`. At first this will return [503](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503). When it returns [200](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200) it means all the services are ready.
If you are writing a script and would like to wait for the local network to come up, you can make a GET request to `http://127.0.0.1:8070`. At first this will return [503](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503). When it returns [200](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200) it means all the services are ready.

You can inspect the response to see which services are ready.

Expand Down
Loading