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 EvmDetails, EvmNetwork, InfraRunOptions, LogFormat, NodeType, TestnetDeployer,
14};
15use colored::Colorize;
16use log::error;
17
18#[derive(Clone)]
24pub struct BootstrapOptions {
25 pub binary_option: BinaryOption,
26 pub chunk_size: Option<u64>,
27 pub enable_logging: bool,
28 pub environment_type: EnvironmentType,
29 pub evm_data_payments_address: Option<String>,
30 pub evm_merkle_payments_address: Option<String>,
31 pub evm_network: EvmNetwork,
32 pub evm_payment_token_address: Option<String>,
33 pub evm_rpc_url: Option<String>,
34 pub full_cone_private_node_count: u16,
35 pub full_cone_private_node_vm_count: Option<u16>,
36 pub full_cone_private_node_volume_size: Option<u16>,
37 pub interval: Duration,
38 pub log_format: Option<LogFormat>,
39 pub max_archived_log_files: u16,
40 pub max_log_files: u16,
41 pub name: String,
42 pub network_contacts_url: Option<String>,
43 pub network_id: u8,
44 pub node_count: u16,
45 pub node_env_variables: Option<Vec<(String, String)>>,
46 pub node_vm_count: Option<u16>,
47 pub node_vm_size: Option<String>,
48 pub node_volume_size: Option<u16>,
49 pub output_inventory_dir_path: PathBuf,
50 pub peer: Option<String>,
51 pub region: String,
52 pub rewards_address: String,
53 pub symmetric_private_node_count: u16,
54 pub symmetric_private_node_vm_count: Option<u16>,
55 pub symmetric_private_node_volume_size: Option<u16>,
56 pub upnp_private_node_count: u16,
57 pub upnp_private_node_vm_count: Option<u16>,
58 pub upnp_private_node_volume_size: Option<u16>,
59}
60
61impl TestnetDeployer {
62 pub async fn bootstrap(&self, options: &BootstrapOptions) -> Result<()> {
63 let build_custom_binaries = options.binary_option.should_provision_build_machine();
64
65 write_environment_details(
66 &self.s3_repository,
67 &options.name,
68 &EnvironmentDetails {
69 deployment_type: DeploymentType::Bootstrap,
70 environment_type: options.environment_type.clone(),
71 evm_details: EvmDetails {
72 network: options.evm_network.clone(),
73 data_payments_address: options.evm_data_payments_address.clone(),
74 merkle_payments_address: options.evm_merkle_payments_address.clone(),
75 payment_token_address: options.evm_payment_token_address.clone(),
76 rpc_url: options.evm_rpc_url.clone(),
77 },
78 funding_wallet_address: None,
79 network_id: Some(options.network_id),
80 region: options.region.clone(),
81 rewards_address: Some(options.rewards_address.clone()),
82 },
83 )
84 .await?;
85
86 self.create_or_update_infra(&InfraRunOptions {
87 client_image_id: None,
88 client_vm_count: Some(0),
89 client_vm_size: None,
90 enable_build_vm: build_custom_binaries,
91 evm_node_count: Some(0),
92 evm_node_vm_size: None,
93 evm_node_image_id: None,
94 full_cone_vm_size: None, full_cone_private_node_vm_count: options.full_cone_private_node_vm_count,
96 full_cone_private_node_volume_size: options.full_cone_private_node_volume_size,
97 genesis_vm_count: Some(0),
98 genesis_node_volume_size: None,
99 name: options.name.clone(),
100 nat_gateway_image_id: None,
101 node_image_id: None,
102 node_vm_count: options.node_vm_count,
103 node_vm_size: options.node_vm_size.clone(),
104 node_volume_size: options.node_volume_size,
105 peer_cache_image_id: None,
106 peer_cache_node_vm_count: Some(0),
107 peer_cache_node_vm_size: None,
108 peer_cache_node_volume_size: None,
109 port_restricted_cone_vm_size: None,
110 port_restricted_private_node_vm_count: Some(0),
111 port_restricted_private_node_volume_size: None,
112 region: options.region.clone(),
113 symmetric_nat_gateway_vm_size: None, symmetric_private_node_vm_count: options.symmetric_private_node_vm_count,
115 symmetric_private_node_volume_size: options.symmetric_private_node_volume_size,
116 tfvars_filenames: Some(
117 options
118 .environment_type
119 .get_tfvars_filenames(&options.name, &options.region),
120 ),
121 upnp_vm_size: None,
122 upnp_private_node_vm_count: options.upnp_private_node_vm_count,
123 upnp_private_node_volume_size: options.upnp_private_node_volume_size,
124 })
125 .map_err(|err| {
126 println!("Failed to create infra {err:?}");
127 err
128 })?;
129
130 let mut provision_options = ProvisionOptions::from(options.clone());
131 if build_custom_binaries {
132 self.ansible_provisioner
133 .print_ansible_run_banner("Build Custom Binaries");
134 self.ansible_provisioner
135 .build_autonomi_binaries(&provision_options, None)
136 .map_err(|err| {
137 println!("Failed to build safe network binaries {err:?}");
138 err
139 })?;
140 }
141
142 let mut failed_to_provision = false;
143
144 self.ansible_provisioner
145 .print_ansible_run_banner("Provision Public Nodes");
146 match self.ansible_provisioner.provision_nodes(
147 &provision_options,
148 options.peer.clone(),
149 options.network_contacts_url.clone(),
150 NodeType::Generic,
151 ) {
152 Ok(()) => {
153 println!("Provisioned public nodes");
154 }
155 Err(e) => {
156 println!("Failed to provision public nodes: {e:?}");
157 failed_to_provision = true;
158 }
159 }
160
161 self.ansible_provisioner
162 .print_ansible_run_banner("Provision UPnP Nodes");
163 match self.ansible_provisioner.provision_nodes(
164 &provision_options,
165 options.peer.clone(),
166 options.network_contacts_url.clone(),
167 NodeType::Upnp,
168 ) {
169 Ok(()) => {
170 println!("Provisioned UPnP nodes");
171 }
172 Err(e) => {
173 error!("Failed to provision UPnP nodes: {e:?}");
174 failed_to_provision = true;
175 }
176 }
177
178 let private_node_inventory = PrivateNodeProvisionInventory::new(
179 &self.ansible_provisioner,
180 options.full_cone_private_node_vm_count,
181 options.symmetric_private_node_vm_count,
182 None, )?;
184
185 if private_node_inventory.should_provision_full_cone_private_nodes() {
186 match self.ansible_provisioner.provision_full_cone(
187 &provision_options,
188 options.peer.clone(),
189 options.network_contacts_url.clone(),
190 private_node_inventory.clone(),
191 None,
192 ) {
193 Ok(()) => {
194 println!("Provisioned full cone nodes and gateway");
195 }
196 Err(err) => {
197 error!("Failed to provision full cone nodes and gateway: {err}");
198 failed_to_provision = true;
199 }
200 }
201 }
202
203 if private_node_inventory.should_provision_symmetric_private_nodes() {
204 self.ansible_provisioner
205 .print_ansible_run_banner("Provision Symmetric NAT Gateway");
206 self.ansible_provisioner
207 .provision_symmetric_nat_gateway(&provision_options, &private_node_inventory)
208 .map_err(|err| {
209 println!("Failed to provision symmetric NAT gateway {err:?}");
210 err
211 })?;
212
213 self.ansible_provisioner
214 .print_ansible_run_banner("Provision Symmetric Private Nodes");
215 match self.ansible_provisioner.provision_symmetric_private_nodes(
216 &mut provision_options,
217 options.peer.clone(),
218 options.network_contacts_url.clone(),
219 &private_node_inventory,
220 ) {
221 Ok(()) => {
222 println!("Provisioned symmetric private nodes");
223 }
224 Err(err) => {
225 error!("Failed to provision symmetric private nodes: {err}");
226 failed_to_provision = true;
227 }
228 }
229 }
230
231 if failed_to_provision {
232 println!("{}", "WARNING!".yellow());
233 println!("Some nodes failed to provision without error.");
234 println!("This usually means a small number of nodes failed to start on a few VMs.");
235 println!("However, most of the time the deployment will still be usable.");
236 println!("See the output from Ansible to determine which VMs had failures.");
237 }
238
239 Ok(())
240 }
241}