1use std::{path::PathBuf, time::Duration};
8
9use crate::{
10 ansible::provisioning::{PrivateNodeProvisionInventory, ProvisionOptions},
11 error::Result,
12 write_environment_details, BinaryOption, DeploymentType, EnvironmentDetails, EnvironmentType,
13 EvmNetwork, InfraRunOptions, LogFormat, NodeType, TestnetDeployer,
14};
15use colored::Colorize;
16use log::error;
17
18#[derive(Clone)]
19pub struct BootstrapOptions {
20 pub binary_option: BinaryOption,
21 pub bootstrap_network_contacts_url: Option<String>,
22 pub bootstrap_peer: Option<String>,
23 pub chunk_size: Option<u64>,
24 pub environment_type: EnvironmentType,
25 pub evm_data_payments_address: Option<String>,
26 pub evm_network: EvmNetwork,
27 pub evm_payment_token_address: Option<String>,
28 pub evm_rpc_url: Option<String>,
29 pub full_cone_private_node_count: u16,
30 pub full_cone_private_node_vm_count: Option<u16>,
31 pub full_cone_private_node_volume_size: Option<u16>,
32 pub interval: Duration,
33 pub log_format: Option<LogFormat>,
34 pub max_archived_log_files: u16,
35 pub max_log_files: u16,
36 pub name: String,
37 pub network_id: Option<u8>,
38 pub node_count: u16,
39 pub node_env_variables: Option<Vec<(String, String)>>,
40 pub node_vm_count: Option<u16>,
41 pub node_vm_size: Option<String>,
42 pub node_volume_size: Option<u16>,
43 pub output_inventory_dir_path: PathBuf,
44 pub rewards_address: String,
45 pub symmetric_private_node_count: u16,
46 pub symmetric_private_node_vm_count: Option<u16>,
47 pub symmetric_private_node_volume_size: Option<u16>,
48}
49
50impl TestnetDeployer {
51 pub async fn bootstrap(&self, options: &BootstrapOptions) -> Result<()> {
52 let build_custom_binaries = {
53 match &options.binary_option {
54 BinaryOption::BuildFromSource { .. } => true,
55 BinaryOption::Versioned { .. } => false,
56 }
57 };
58
59 write_environment_details(
60 &self.s3_repository,
61 &options.name,
62 &EnvironmentDetails {
63 deployment_type: DeploymentType::Bootstrap,
64 environment_type: options.environment_type.clone(),
65 evm_network: options.evm_network.clone(),
66 evm_data_payments_address: options.evm_data_payments_address.clone(),
67 evm_payment_token_address: options.evm_payment_token_address.clone(),
68 evm_rpc_url: options.evm_rpc_url.clone(),
69 funding_wallet_address: None,
70 network_id: options.network_id,
71 rewards_address: options.rewards_address.clone(),
72 },
73 )
74 .await?;
75
76 self.create_or_update_infra(&InfraRunOptions {
77 enable_build_vm: build_custom_binaries,
78 evm_node_count: Some(0),
79 evm_node_vm_size: None,
80 full_cone_nat_gateway_vm_size: None, full_cone_private_node_vm_count: options.full_cone_private_node_vm_count,
82 full_cone_private_node_volume_size: options.full_cone_private_node_volume_size,
83 genesis_vm_count: Some(0),
84 genesis_node_volume_size: None,
85 name: options.name.clone(),
86 node_vm_count: options.node_vm_count,
87 node_vm_size: options.node_vm_size.clone(),
88 node_volume_size: options.node_volume_size,
89 peer_cache_node_vm_count: Some(0),
90 peer_cache_node_vm_size: None,
91 peer_cache_node_volume_size: None,
92 symmetric_nat_gateway_vm_size: None, symmetric_private_node_vm_count: options.symmetric_private_node_vm_count,
94 symmetric_private_node_volume_size: options.symmetric_private_node_volume_size,
95 tfvars_filename: options
96 .environment_type
97 .get_tfvars_filename(&options.name)
98 .to_string(),
99 uploader_vm_count: Some(0),
100 uploader_vm_size: None,
101 })
102 .map_err(|err| {
103 println!("Failed to create infra {err:?}");
104 err
105 })?;
106
107 let mut provision_options = ProvisionOptions::from(options.clone());
108 if build_custom_binaries {
109 self.ansible_provisioner
110 .print_ansible_run_banner("Build Custom Binaries");
111 self.ansible_provisioner
112 .build_safe_network_binaries(&provision_options)
113 .map_err(|err| {
114 println!("Failed to build safe network binaries {err:?}");
115 err
116 })?;
117 }
118
119 let mut failed_to_provision = false;
120
121 self.ansible_provisioner
122 .print_ansible_run_banner("Provision Normal Nodes");
123 match self.ansible_provisioner.provision_nodes(
124 &provision_options,
125 options.bootstrap_peer.clone(),
126 options.bootstrap_network_contacts_url.clone(),
127 NodeType::Generic,
128 ) {
129 Ok(()) => {
130 println!("Provisioned normal nodes");
131 }
132 Err(e) => {
133 println!("Failed to provision normal nodes: {e:?}");
134 failed_to_provision = true;
135 }
136 }
137
138 let private_node_inventory = PrivateNodeProvisionInventory::new(
139 &self.ansible_provisioner,
140 options.full_cone_private_node_vm_count,
141 options.symmetric_private_node_vm_count,
142 )?;
143
144 if private_node_inventory.should_provision_full_cone_private_nodes() {
145 match self.ansible_provisioner.provision_full_cone(
146 &provision_options,
147 options.bootstrap_peer.clone(),
148 options.bootstrap_network_contacts_url.clone(),
149 private_node_inventory.clone(),
150 None,
151 ) {
152 Ok(()) => {
153 println!("Provisioned Full Cone nodes and Gateway");
154 }
155 Err(err) => {
156 error!("Failed to provision Full Cone nodes and Gateway: {err}");
157 failed_to_provision = true;
158 }
159 }
160 }
161
162 if private_node_inventory.should_provision_symmetric_private_nodes() {
163 self.ansible_provisioner
164 .print_ansible_run_banner("Provision Symmetric NAT Gateway");
165 self.ansible_provisioner
166 .provision_symmetric_nat_gateway(&provision_options, &private_node_inventory)
167 .map_err(|err| {
168 println!("Failed to provision Symmetric NAT gateway {err:?}");
169 err
170 })?;
171
172 self.ansible_provisioner
173 .print_ansible_run_banner("Provision Symmetric Private Nodes");
174 match self.ansible_provisioner.provision_symmetric_private_nodes(
175 &mut provision_options,
176 options.bootstrap_peer.clone(),
177 options.bootstrap_network_contacts_url.clone(),
178 &private_node_inventory,
179 ) {
180 Ok(()) => {
181 println!("Provisioned Symmetric private nodes");
182 }
183 Err(err) => {
184 error!("Failed to provision Symmetric Private nodes: {err}");
185 failed_to_provision = true;
186 }
187 }
188 }
189
190 if failed_to_provision {
191 println!("{}", "WARNING!".yellow());
192 println!("Some nodes failed to provision without error.");
193 println!("This usually means a small number of nodes failed to start on a few VMs.");
194 println!("However, most of the time the deployment will still be usable.");
195 println!("See the output from Ansible to determine which VMs had failures.");
196 }
197
198 Ok(())
199 }
200}