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