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