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}
50
51impl TestnetDeployer {
52 pub async fn bootstrap(&self, options: &BootstrapOptions) -> Result<()> {
53 let build_custom_binaries = {
54 match &options.binary_option {
55 BinaryOption::BuildFromSource { .. } => true,
56 BinaryOption::Versioned { .. } => false,
57 }
58 };
59
60 write_environment_details(
61 &self.s3_repository,
62 &options.name,
63 &EnvironmentDetails {
64 deployment_type: DeploymentType::Bootstrap,
65 environment_type: options.environment_type.clone(),
66 evm_details: EvmDetails {
67 network: options.evm_network.clone(),
68 data_payments_address: options.evm_data_payments_address.clone(),
69 payment_token_address: options.evm_payment_token_address.clone(),
70 rpc_url: options.evm_rpc_url.clone(),
71 },
72 funding_wallet_address: None,
73 network_id: Some(options.network_id),
74 region: options.region.clone(),
75 rewards_address: Some(options.rewards_address.clone()),
76 },
77 )
78 .await?;
79
80 self.create_or_update_infra(&InfraRunOptions {
81 client_image_id: None,
82 client_vm_count: Some(0),
83 client_vm_size: None,
84 enable_build_vm: build_custom_binaries,
85 evm_node_count: Some(0),
86 evm_node_vm_size: None,
87 evm_node_image_id: None,
88 full_cone_nat_gateway_vm_size: None, full_cone_private_node_vm_count: options.full_cone_private_node_vm_count,
90 full_cone_private_node_volume_size: options.full_cone_private_node_volume_size,
91 genesis_vm_count: Some(0),
92 genesis_node_volume_size: None,
93 name: options.name.clone(),
94 nat_gateway_image_id: None,
95 node_image_id: None,
96 node_vm_count: options.node_vm_count,
97 node_vm_size: options.node_vm_size.clone(),
98 node_volume_size: options.node_volume_size,
99 peer_cache_image_id: None,
100 peer_cache_node_vm_count: Some(0),
101 peer_cache_node_vm_size: None,
102 peer_cache_node_volume_size: None,
103 region: options.region.clone(),
104 symmetric_nat_gateway_vm_size: None, symmetric_private_node_vm_count: options.symmetric_private_node_vm_count,
106 symmetric_private_node_volume_size: options.symmetric_private_node_volume_size,
107 tfvars_filenames: Some(
108 options
109 .environment_type
110 .get_tfvars_filenames(&options.name, &options.region),
111 ),
112 })
113 .map_err(|err| {
114 println!("Failed to create infra {err:?}");
115 err
116 })?;
117
118 let mut provision_options = ProvisionOptions::from(options.clone());
119 if build_custom_binaries {
120 self.ansible_provisioner
121 .print_ansible_run_banner("Build Custom Binaries");
122 self.ansible_provisioner
123 .build_safe_network_binaries(&provision_options, None)
124 .map_err(|err| {
125 println!("Failed to build safe network binaries {err:?}");
126 err
127 })?;
128 }
129
130 let mut failed_to_provision = false;
131
132 self.ansible_provisioner
133 .print_ansible_run_banner("Provision Normal Nodes");
134 match self.ansible_provisioner.provision_nodes(
135 &provision_options,
136 options.peer.clone(),
137 options.network_contacts_url.clone(),
138 NodeType::Generic,
139 ) {
140 Ok(()) => {
141 println!("Provisioned normal nodes");
142 }
143 Err(e) => {
144 println!("Failed to provision normal nodes: {e:?}");
145 failed_to_provision = true;
146 }
147 }
148
149 let private_node_inventory = PrivateNodeProvisionInventory::new(
150 &self.ansible_provisioner,
151 options.full_cone_private_node_vm_count,
152 options.symmetric_private_node_vm_count,
153 )?;
154
155 if private_node_inventory.should_provision_full_cone_private_nodes() {
156 match self.ansible_provisioner.provision_full_cone(
157 &provision_options,
158 options.peer.clone(),
159 options.network_contacts_url.clone(),
160 private_node_inventory.clone(),
161 None,
162 ) {
163 Ok(()) => {
164 println!("Provisioned Full Cone nodes and Gateway");
165 }
166 Err(err) => {
167 error!("Failed to provision Full Cone nodes and Gateway: {err}");
168 failed_to_provision = true;
169 }
170 }
171 }
172
173 if private_node_inventory.should_provision_symmetric_private_nodes() {
174 self.ansible_provisioner
175 .print_ansible_run_banner("Provision Symmetric NAT Gateway");
176 self.ansible_provisioner
177 .provision_symmetric_nat_gateway(&provision_options, &private_node_inventory)
178 .map_err(|err| {
179 println!("Failed to provision Symmetric NAT gateway {err:?}");
180 err
181 })?;
182
183 self.ansible_provisioner
184 .print_ansible_run_banner("Provision Symmetric Private Nodes");
185 match self.ansible_provisioner.provision_symmetric_private_nodes(
186 &mut provision_options,
187 options.peer.clone(),
188 options.network_contacts_url.clone(),
189 &private_node_inventory,
190 ) {
191 Ok(()) => {
192 println!("Provisioned Symmetric private nodes");
193 }
194 Err(err) => {
195 error!("Failed to provision Symmetric Private nodes: {err}");
196 failed_to_provision = true;
197 }
198 }
199 }
200
201 if failed_to_provision {
202 println!("{}", "WARNING!".yellow());
203 println!("Some nodes failed to provision without error.");
204 println!("This usually means a small number of nodes failed to start on a few VMs.");
205 println!("However, most of the time the deployment will still be usable.");
206 println!("See the output from Ansible to determine which VMs had failures.");
207 }
208
209 Ok(())
210 }
211}