use super::{get_options_from_s3, OptionsType};
use clap::Subcommand;
use color_eyre::Result;
use sn_testnet_deploy::{
ansible::provisioning::{PrivateNodeProvisionInventory, ProvisionOptions},
deploy::DeployOptions,
error::Error,
get_bootstrap_cache_url, get_genesis_multiaddr,
inventory::DeploymentInventoryService,
CloudProvider, NodeType, TestnetDeployBuilder,
};
#[derive(Subcommand, Debug)]
pub enum ProvisionCommands {
#[clap(name = "clients")]
Clients {
#[arg(short = 'n', long)]
name: String,
},
#[clap(name = "full-cone-private-nodes")]
FullConePrivateNodes {
#[arg(short = 'n', long)]
name: String,
#[arg(long, default_value = "false")]
disable_nodes: bool,
},
#[clap(name = "port-restricted-cone-private-nodes")]
PortRestrictedConePrivateNodes {
#[arg(short = 'n', long)]
name: String,
#[arg(long, default_value = "false")]
disable_nodes: bool,
},
#[clap(name = "generic-nodes")]
GenericNodes {
#[arg(short = 'n', long)]
name: String,
#[arg(long, default_value = "false")]
disable_nodes: bool,
},
#[clap(name = "peer-cache-nodes")]
PeerCacheNodes {
#[arg(short = 'n', long)]
name: String,
#[arg(long, default_value = "false")]
disable_nodes: bool,
},
#[clap(name = "symmetric-private-nodes")]
SymmetricPrivateNodes {
#[arg(short = 'n', long)]
name: String,
#[arg(long, default_value = "false")]
disable_nodes: bool,
},
#[clap(name = "upnp-nodes")]
UpnpNodes {
#[arg(short = 'n', long)]
name: String,
#[arg(long, default_value = "false")]
disable_nodes: bool,
},
}
async fn init_provision(
name: &str,
) -> Result<(
DeployOptions,
ProvisionOptions,
sn_testnet_deploy::ansible::provisioning::AnsibleProvisioner,
sn_testnet_deploy::ssh::SshClient,
)> {
let deploy_options: DeployOptions = get_options_from_s3(name, OptionsType::Deploy).await?;
let provision_options: ProvisionOptions =
get_options_from_s3(name, OptionsType::Provision).await?;
let mut builder = TestnetDeployBuilder::default();
builder
.ansible_verbose_mode(false)
.deployment_type(deploy_options.environment_type.clone())
.environment_name(name)
.provider(CloudProvider::DigitalOcean);
let testnet_deployer = builder.build()?;
let inventory_service = DeploymentInventoryService::from(&testnet_deployer);
inventory_service
.generate_or_retrieve_inventory(name, true, Some(deploy_options.binary_option.clone()))
.await?;
let provisioner = testnet_deployer.ansible_provisioner;
let ssh_client = testnet_deployer.ssh_client;
Ok((deploy_options, provision_options, provisioner, ssh_client))
}
async fn handle_provision_nodes(
name: String,
node_type: NodeType,
disable_nodes: bool,
) -> Result<()> {
let (deploy_options, mut provision_options, provisioner, ssh_client) =
init_provision(&name).await?;
provision_options.disable_nodes = disable_nodes;
let (genesis_multiaddr, genesis_ip) =
get_genesis_multiaddr(&provisioner.ansible_runner, &ssh_client)?
.ok_or_else(|| Error::GenesisListenAddress)?;
let genesis_network_contacts = get_bootstrap_cache_url(&genesis_ip);
let private_node_inventory = PrivateNodeProvisionInventory::new(
&provisioner,
deploy_options.full_cone_private_node_vm_count,
deploy_options.symmetric_private_node_vm_count,
Some(deploy_options.port_restricted_cone_private_node_vm_count),
)?;
match node_type {
NodeType::FullConePrivateNode => {
if private_node_inventory.should_provision_full_cone_private_nodes() {
provisioner.provision_full_cone(
&provision_options,
Some(genesis_multiaddr),
Some(genesis_network_contacts),
private_node_inventory,
None,
)?;
} else {
println!("Full cone private nodes have not been requested for this environment");
}
}
NodeType::PortRestrictedConePrivateNode => {
if private_node_inventory.should_provision_port_restricted_cone_private_nodes() {
provisioner.provision_port_restricted_cone(
&provision_options,
Some(genesis_multiaddr),
Some(genesis_network_contacts),
private_node_inventory,
None,
)?;
} else {
println!("Port restricted cone private nodes have not been requested for this environment");
}
}
NodeType::SymmetricPrivateNode => {
if private_node_inventory.should_provision_symmetric_private_nodes() {
provisioner.print_ansible_run_banner("Provision Symmetric NAT Gateway");
provisioner
.provision_symmetric_nat_gateway(&provision_options, &private_node_inventory)
.map_err(|err| {
println!("Failed to provision Symmetric NAT gateway {err:?}");
err
})?;
provisioner.print_ansible_run_banner("Provision Symmetric Private Nodes");
provisioner.provision_symmetric_private_nodes(
&mut provision_options,
Some(genesis_multiaddr),
Some(genesis_network_contacts),
&private_node_inventory,
)?;
} else {
println!("Symmetric private nodes have not been requested for this environment");
}
}
_ => {
provisioner.print_ansible_run_banner(&format!("Provision {node_type} Nodes"));
provisioner.provision_nodes(
&provision_options,
Some(genesis_multiaddr),
Some(genesis_network_contacts),
node_type,
)?;
}
}
Ok(())
}
pub async fn handle_provision_peer_cache_nodes(name: String, disable_nodes: bool) -> Result<()> {
handle_provision_nodes(name, NodeType::PeerCache, disable_nodes).await
}
pub async fn handle_provision_generic_nodes(name: String, disable_nodes: bool) -> Result<()> {
handle_provision_nodes(name, NodeType::Generic, disable_nodes).await
}
pub async fn handle_provision_symmetric_private_nodes(
name: String,
disable_nodes: bool,
) -> Result<()> {
handle_provision_nodes(name, NodeType::SymmetricPrivateNode, disable_nodes).await
}
pub async fn handle_provision_full_cone_private_nodes(
name: String,
disable_nodes: bool,
) -> Result<()> {
handle_provision_nodes(name, NodeType::FullConePrivateNode, disable_nodes).await
}
pub async fn handle_provision_port_restricted_cone_private_nodes(
name: String,
disable_nodes: bool,
) -> Result<()> {
handle_provision_nodes(name, NodeType::PortRestrictedConePrivateNode, disable_nodes).await
}
pub async fn handle_provision_upnp_nodes(name: String, disable_nodes: bool) -> Result<()> {
handle_provision_nodes(name, NodeType::Upnp, disable_nodes).await
}
pub async fn handle_provision_clients(name: String) -> Result<()> {
let (_, provision_options, provisioner, ssh_client) = init_provision(&name).await?;
let (genesis_multiaddr, genesis_ip) =
get_genesis_multiaddr(&provisioner.ansible_runner, &ssh_client)?
.ok_or_else(|| Error::GenesisListenAddress)?;
let genesis_network_contacts = get_bootstrap_cache_url(&genesis_ip);
provisioner.print_ansible_run_banner("Provision Uploaders");
provisioner
.provision_uploaders(
&provision_options,
Some(genesis_multiaddr.clone()),
Some(genesis_network_contacts.clone()),
)
.await
.map_err(|err| {
println!("Failed to provision clients: {err:?}");
err
})?;
provisioner.print_ansible_run_banner("Provision Downloaders");
provisioner
.provision_downloaders(
&provision_options,
Some(genesis_multiaddr.clone()),
Some(genesis_network_contacts.clone()),
)
.await
.map_err(|err| {
println!("Failed to provision downloaders: {err:?}");
err
})?;
provisioner.print_ansible_run_banner("Provision Chunk Trackers");
provisioner
.provision_chunk_trackers(
&provision_options,
Some(genesis_multiaddr.clone()),
Some(genesis_network_contacts.clone()),
)
.await
.map_err(|err| {
println!("Failed to provision chunk trackers {err:?}");
err
})?;
Ok(())
}