sn_testnet_deploy/
bootstrap.rs

1// Copyright (c) 2023, MaidSafe.
2// All rights reserved.
3//
4// This SAFE Network Software is licensed under the BSD-3-Clause license.
5// Please see the LICENSE file for more details.
6
7use 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, // We can take the value from tfvars for bootstrap deployments.
81            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, // We can take the value from tfvars for bootstrap deployments.
93            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}