Add DNS resolution to cli tools

This commit is contained in:
Michael Vines 2019-04-13 19:34:27 -07:00 committed by Grimes
parent 6859907df9
commit 0767c0c07f
13 changed files with 100 additions and 57 deletions

3
Cargo.lock generated
View File

@ -2200,6 +2200,7 @@ dependencies = [
"solana-drone 0.13.0", "solana-drone 0.13.0",
"solana-logger 0.13.0", "solana-logger 0.13.0",
"solana-metrics 0.13.0", "solana-metrics 0.13.0",
"solana-netutil 0.13.0",
"solana-sdk 0.13.0", "solana-sdk 0.13.0",
] ]
@ -2388,6 +2389,7 @@ dependencies = [
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"solana 0.13.0", "solana 0.13.0",
"solana-netutil 0.13.0",
"solana-sdk 0.13.0", "solana-sdk 0.13.0",
] ]
@ -2690,6 +2692,7 @@ dependencies = [
"solana-client 0.13.0", "solana-client 0.13.0",
"solana-drone 0.13.0", "solana-drone 0.13.0",
"solana-logger 0.13.0", "solana-logger 0.13.0",
"solana-netutil 0.13.0",
"solana-sdk 0.13.0", "solana-sdk 0.13.0",
"solana-vote-api 0.13.0", "solana-vote-api 0.13.0",
"solana-vote-signer 0.13.0", "solana-vote-signer 0.13.0",

View File

@ -16,6 +16,7 @@ solana-client = { path = "../client", version = "0.13.0" }
solana-drone = { path = "../drone", version = "0.13.0" } solana-drone = { path = "../drone", version = "0.13.0" }
solana-logger = { path = "../logger", version = "0.13.0" } solana-logger = { path = "../logger", version = "0.13.0" }
solana-metrics = { path = "../metrics", version = "0.13.0" } solana-metrics = { path = "../metrics", version = "0.13.0" }
solana-netutil = { path = "../netutil", version = "0.13.0" }
solana-sdk = { path = "../sdk", version = "0.13.0" } solana-sdk = { path = "../sdk", version = "0.13.0" }
[features] [features]

View File

@ -117,14 +117,14 @@ pub fn extract_args<'a>(matches: &ArgMatches<'a>) -> Config {
let mut args = Config::default(); let mut args = Config::default();
if let Some(addr) = matches.value_of("network") { if let Some(addr) = matches.value_of("network") {
args.network_addr = addr.parse().unwrap_or_else(|e| { args.network_addr = solana_netutil::parse_host_port(addr).unwrap_or_else(|e| {
eprintln!("failed to parse network: {}", e); eprintln!("failed to parse network address: {}", e);
exit(1) exit(1)
}); });
} }
if let Some(addr) = matches.value_of("drone") { if let Some(addr) = matches.value_of("drone") {
args.drone_addr = addr.parse().unwrap_or_else(|e| { args.drone_addr = solana_netutil::parse_host_port(addr).unwrap_or_else(|e| {
eprintln!("failed to parse drone address: {}", e); eprintln!("failed to parse drone address: {}", e);
exit(1) exit(1)
}); });

View File

@ -161,7 +161,7 @@ This will dump all the threads stack traces into gdb.txt
In this example the client connects to our public testnet. To run validators on the testnet you would need to open udp ports `8000-10000`. In this example the client connects to our public testnet. To run validators on the testnet you would need to open udp ports `8000-10000`.
```bash ```bash
$ ./multinode-demo/client.sh --network $(dig +short testnet.solana.com):8001 --duration 60 $ ./multinode-demo/client.sh --network testnet.solana.com:8001 --duration 60
``` ```
You can observe the effects of your client's transactions on our [dashboard](https://metrics.solana.com:3000/d/testnet/testnet-hud?orgId=2&from=now-30m&to=now&refresh=5s&var-testnet=testnet) You can observe the effects of your client's transactions on our [dashboard](https://metrics.solana.com:3000/d/testnet/testnet-hud?orgId=2&from=now-30m&to=now&refresh=5s&var-testnet=testnet)

View File

@ -26,14 +26,7 @@ Prebuilt binaries are available for Linux x86_64 (Ubuntu 18.04 recommended).
MacOS or WSL users may build from source. MacOS or WSL users may build from source.
### Validator Setup ### Validator Setup
The shell commands in this section assume the following environment variables are
set:
```bash
$ export ip=$(dig +short beta.testnet.solana.com)
```
#### Obtaining The Software #### Obtaining The Software
##### Bootstrap with `solana-install` ##### Bootstrap with `solana-install`
The `solana-install` tool can be used to easily install and upgrade the cluster The `solana-install` tool can be used to easily install and upgrade the cluster
@ -82,8 +75,8 @@ just restarting itself before debugging further.
Receive an airdrop of lamports from the testnet drone: Receive an airdrop of lamports from the testnet drone:
```bash ```bash
$ solana-wallet -n ${ip:?} airdrop 123 $ solana-wallet -n beta.testnet.solana.com airdrop 123
$ solana-wallet -n ${ip:?} balance $ solana-wallet -n beta.testnet.solana.com balance
``` ```
Fetch the current testnet transaction count over JSON RPC: Fetch the current testnet transaction count over JSON RPC:
@ -95,7 +88,7 @@ Inspect the blockexplorer at http://beta.testnet.solana.com/ for activity.
Run the following command to join the gossip network and view all the other nodes in the cluster: Run the following command to join the gossip network and view all the other nodes in the cluster:
```bash ```bash
$ RUST_LOG=info solana-gossip --network ${ip:?}:8001 $ solana-gossip --network beta.testnet.solana.com:8001
``` ```
### Starting The Validator ### Starting The Validator
@ -103,24 +96,24 @@ The following command will start a new validator node.
If this is a `solana-install`-installation: If this is a `solana-install`-installation:
```bash ```bash
$ fullnode-x.sh --public-address --poll-for-new-genesis-block ${ip:?} $ fullnode-x.sh --public-address --poll-for-new-genesis-block beta.testnet.solana.com:8001
``` ```
Alternatively, the `solana-install run` command can be used to run the validator Alternatively, the `solana-install run` command can be used to run the validator
node while periodically checking for and applying software updates: node while periodically checking for and applying software updates:
```bash ```bash
$ solana-install run fullnode-x.sh --public-address --poll-for-new-genesis-block ${ip:?} $ solana-install run fullnode-x.sh --public-address --poll-for-new-genesis-block beta.testnet.solana.com:8001
``` ```
When not using `solana-install`: When not using `solana-install`:
```bash ```bash
$ USE_INSTALL=1 ./multinode-demo/fullnode-x.sh --public-address --poll-for-new-genesis-block ${ip:?} $ USE_INSTALL=1 ./multinode-demo/fullnode-x.sh --public-address --poll-for-new-genesis-block beta.testnet.solana.com:8001
``` ```
Then from another console, confirm the IP address if your node is now visible in Then from another console, confirm the IP address if your node is now visible in
the gossip network by running: the gossip network by running:
```bash ```bash
$ RUST_LOG=info solana-gossip --network ${ip:?}:8001 $ solana-gossip --network beta.testnet.solana.com:8001
``` ```
Congratulations, you're now participating in the testnet cluster! Congratulations, you're now participating in the testnet cluster!

View File

@ -187,9 +187,9 @@ fn main() {
if matches.is_present("enable_rpc_exit") { if matches.is_present("enable_rpc_exit") {
fullnode_config.rpc_config.enable_fullnode_exit = true; fullnode_config.rpc_config.enable_fullnode_exit = true;
} }
fullnode_config.rpc_config.drone_addr = matches fullnode_config.rpc_config.drone_addr = matches.value_of("rpc_drone_address").map(|address| {
.value_of("rpc_drone_address") solana_netutil::parse_host_port(address).expect("failed to parse drone address")
.map(|address| address.parse().expect("failed to parse drone address")); });
let dynamic_port_range = parse_port_range(matches.value_of("dynamic_port_range").unwrap()) let dynamic_port_range = parse_port_range(matches.value_of("dynamic_port_range").unwrap())
.expect("invalid dynamic_port_range"); .expect("invalid dynamic_port_range");
@ -214,7 +214,8 @@ fn main() {
fullnode_config.account_paths = None; fullnode_config.account_paths = None;
} }
let cluster_entrypoint = matches.value_of("network").map(|network| { let cluster_entrypoint = matches.value_of("network").map(|network| {
let gossip_addr = network.parse().expect("failed to parse network address"); let gossip_addr =
solana_netutil::parse_host_port(network).expect("failed to parse network address");
ContactInfo::new_gossip_entry_point(&gossip_addr) ContactInfo::new_gossip_entry_point(&gossip_addr)
}); });
let (_signer_service, _signer_addr) = if let Some(signer_addr) = matches.value_of("signer") { let (_signer_service, _signer_addr) = if let Some(signer_addr) = matches.value_of("signer") {

View File

@ -12,6 +12,7 @@ homepage = "https://solana.com/"
clap = "2.33.0" clap = "2.33.0"
env_logger = "0.6.1" env_logger = "0.6.1"
solana = { path = "../core", version = "0.13.0" } solana = { path = "../core", version = "0.13.0" }
solana-netutil = { path = "../netutil", version = "0.13.0" }
solana-sdk = { path = "../sdk", version = "0.13.0" } solana-sdk = { path = "../sdk", version = "0.13.0" }
[features] [features]

View File

@ -67,8 +67,8 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.get_matches(); .get_matches();
if let Some(addr) = matches.value_of("network") { if let Some(addr) = matches.value_of("network") {
network_addr = addr.parse().unwrap_or_else(|e| { network_addr = solana_netutil::parse_host_port(addr).unwrap_or_else(|e| {
eprintln!("failed to parse network: {}", e); eprintln!("failed to parse network address: {}", e);
exit(1) exit(1)
}); });
} }

View File

@ -78,19 +78,7 @@ find_leader() {
leader_address=127.0.0.1:8001 # Default to local leader leader_address=127.0.0.1:8001 # Default to local leader
elif [[ -z $2 ]]; then elif [[ -z $2 ]]; then
leader=$1 leader=$1
leader_address=$leader:8001
declare leader_ip
if [[ $leader =~ ^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$ ]]; then
leader_ip=$leader
else
leader_ip=$(dig +short "${leader%:*}" | head -n1)
if [[ -z $leader_ip ]]; then
usage "Error: unable to resolve IP address for $leader"
fi
fi
leader_address=$leader_ip:8001
shift=1 shift=1
else else
leader=$1 leader=$1

View File

@ -7,7 +7,7 @@ use rand::{thread_rng, Rng};
use reqwest; use reqwest;
use socket2::{Domain, SockAddr, Socket, Type}; use socket2::{Domain, SockAddr, Socket, Type};
use std::io; use std::io;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener, UdpSocket}; use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener, ToSocketAddrs, UdpSocket};
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
/// A data type representing a public Udp socket /// A data type representing a public Udp socket
@ -70,6 +70,31 @@ pub fn parse_port_range(port_range: &str) -> Option<PortRange> {
Some((start_port, end_port)) Some((start_port, end_port))
} }
pub fn parse_host(host: &str) -> Result<IpAddr, String> {
let ips: Vec<_> = (host, 0)
.to_socket_addrs()
.map_err(|err| err.to_string())?
.map(|socket_address| socket_address.ip())
.collect();
if ips.is_empty() {
Err(format!("Unable to resolve host: {}", host))
} else {
Ok(ips[0])
}
}
pub fn parse_host_port(host_port: &str) -> Result<SocketAddr, String> {
let addrs: Vec<_> = host_port
.to_socket_addrs()
.map_err(|err| err.to_string())?
.collect();
if addrs.is_empty() {
Err(format!("Unable to resolve host: {}", host_port))
} else {
Ok(addrs[0])
}
}
fn find_eth0ish_ip_addr( fn find_eth0ish_ip_addr(
ifaces: &mut Vec<datalink::NetworkInterface>, ifaces: &mut Vec<datalink::NetworkInterface>,
enable_ipv6: bool, enable_ipv6: bool,
@ -334,6 +359,22 @@ mod tests {
assert_eq!(parse_port_range("2-1"), None); assert_eq!(parse_port_range("2-1"), None);
} }
#[test]
fn test_parse_host() {
parse_host("localhost:1234").unwrap_err();
parse_host("localhost").unwrap();
parse_host("127.0.0.0:1234").unwrap_err();
parse_host("127.0.0.0").unwrap();
}
#[test]
fn test_parse_host_port() {
parse_host_port("localhost:1234").unwrap();
parse_host_port("localhost").unwrap_err();
parse_host_port("127.0.0.0:1234").unwrap();
parse_host_port("127.0.0.0").unwrap_err();
}
#[test] #[test]
fn test_bind() { fn test_bind() {
assert_eq!(bind_in_range((2000, 2001)).unwrap().0, 2000); assert_eq!(bind_in_range((2000, 2001)).unwrap().0, 2000);

View File

@ -78,7 +78,9 @@ fn main() {
let network_addr = matches let network_addr = matches
.value_of("network") .value_of("network")
.map(|network| network.parse().expect("failed to parse network address")) .map(|network| {
solana_netutil::parse_host_port(network).expect("failed to parse network address")
})
.unwrap(); .unwrap();
let leader_info = ContactInfo::new_gossip_entry_point(&network_addr); let leader_info = ContactInfo::new_gossip_entry_point(&network_addr);
@ -94,6 +96,5 @@ fn main() {
.unwrap(); .unwrap();
replicator.run(); replicator.run();
replicator.close(); replicator.close();
} }

View File

@ -20,6 +20,7 @@ solana-budget-api = { path = "../programs/budget_api", version = "0.13.0" }
solana-client = { path = "../client", version = "0.13.0" } solana-client = { path = "../client", version = "0.13.0" }
solana-drone = { path = "../drone", version = "0.13.0" } solana-drone = { path = "../drone", version = "0.13.0" }
solana-logger = { path = "../logger", version = "0.13.0" } solana-logger = { path = "../logger", version = "0.13.0" }
solana-netutil = { path = "../netutil", version = "0.13.0" }
solana-sdk = { path = "../sdk", version = "0.13.0" } solana-sdk = { path = "../sdk", version = "0.13.0" }
solana-vote-api = { path = "../programs/vote_api", version = "0.13.0" } solana-vote-api = { path = "../programs/vote_api", version = "0.13.0" }
solana-vote-signer = { path = "../vote-signer", version = "0.13.0" } solana-vote-signer = { path = "../vote-signer", version = "0.13.0" }

View File

@ -7,28 +7,31 @@ use solana_wallet::wallet::{parse_command, process_command, WalletConfig, Wallet
use std::error; use std::error;
pub fn parse_args(matches: &ArgMatches<'_>) -> Result<WalletConfig, Box<dyn error::Error>> { pub fn parse_args(matches: &ArgMatches<'_>) -> Result<WalletConfig, Box<dyn error::Error>> {
let host = matches let host = solana_netutil::parse_host(matches.value_of("host").unwrap()).or_else(|err| {
.value_of("host") Err(WalletError::BadParameter(format!(
.unwrap() "Invalid host: {:?}",
.parse() err
.or_else(|_| Err(WalletError::BadParameter("Invalid host".to_string())))?; )))
})?;
let drone_host = if let Some(drone_host) = matches.value_of("drone_host") { let drone_host = if let Some(drone_host) = matches.value_of("drone_host") {
Some( Some(solana_netutil::parse_host(drone_host).or_else(|err| {
drone_host Err(WalletError::BadParameter(format!(
.parse() "Invalid drone host: {:?}",
.or_else(|_| Err(WalletError::BadParameter("Invalid drone host".to_string())))?, err
) )))
})?)
} else { } else {
None None
}; };
let rpc_host = if let Some(rpc_host) = matches.value_of("rpc_host") { let rpc_host = if let Some(rpc_host) = matches.value_of("rpc_host") {
Some( Some(solana_netutil::parse_host(rpc_host).or_else(|err| {
rpc_host Err(WalletError::BadParameter(format!(
.parse() "Invalid rpc host: {:?}",
.or_else(|_| Err(WalletError::BadParameter("Invalid rpc host".to_string())))?, err
) )))
})?)
} else { } else {
None None
}; };
@ -37,13 +40,23 @@ pub fn parse_args(matches: &ArgMatches<'_>) -> Result<WalletConfig, Box<dyn erro
.value_of("drone_port") .value_of("drone_port")
.unwrap() .unwrap()
.parse() .parse()
.or_else(|_| Err(WalletError::BadParameter("Invalid drone port".to_string())))?; .or_else(|err| {
Err(WalletError::BadParameter(format!(
"Invalid drone port: {:?}",
err
)))
})?;
let rpc_port = matches let rpc_port = matches
.value_of("rpc_port") .value_of("rpc_port")
.unwrap() .unwrap()
.parse() .parse()
.or_else(|_| Err(WalletError::BadParameter("Invalid rpc port".to_string())))?; .or_else(|err| {
Err(WalletError::BadParameter(format!(
"Invalid rpc port: {:?}",
err
)))
})?;
let mut path = dirs::home_dir().expect("home directory"); let mut path = dirs::home_dir().expect("home directory");
let id_path = if matches.is_present("keypair") { let id_path = if matches.is_present("keypair") {