ant_node_manager/cmd/
mod.rs

1// Copyright (C) 2024 MaidSafe.net limited.
2//
3// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
4// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
5// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
6// KIND, either express or implied. Please review the Licences for the specific language governing
7// permissions and limitations relating to use of the SAFE Network Software.
8
9pub mod daemon;
10pub mod local;
11pub mod nat_detection;
12pub mod node;
13
14use crate::{
15    helpers::{download_and_extract_release, get_bin_version},
16    print_banner, VerbosityLevel,
17};
18use ant_releases::{AntReleaseRepoActions, ReleaseType};
19use ant_service_management::UpgradeResult;
20use color_eyre::{eyre::eyre, Result};
21use colored::Colorize;
22use semver::Version;
23use std::{
24    path::PathBuf,
25    process::{Command, Stdio},
26};
27
28pub async fn download_and_get_upgrade_bin_path(
29    custom_bin_path: Option<PathBuf>,
30    release_type: ReleaseType,
31    url: Option<String>,
32    version: Option<String>,
33    verbosity: VerbosityLevel,
34) -> Result<(PathBuf, Version)> {
35    if let Some(path) = custom_bin_path {
36        debug!(
37            "Using the supplied custom binary at {}",
38            path.to_string_lossy()
39        );
40        if verbosity != VerbosityLevel::Minimal {
41            println!(
42                "Using the supplied custom binary at {}",
43                path.to_string_lossy()
44            );
45        }
46        let bin_version = get_bin_version(&path)?;
47        return Ok((path, bin_version.parse()?));
48    }
49
50    let release_repo = <dyn AntReleaseRepoActions>::default_config();
51    if let Some(version) = version {
52        debug!("Downloading provided version {version} of {release_type}");
53        let (upgrade_bin_path, version) = download_and_extract_release(
54            release_type,
55            None,
56            Some(version),
57            &*release_repo,
58            verbosity,
59            None,
60        )
61        .await?;
62        Ok((upgrade_bin_path, Version::parse(&version)?))
63    } else if let Some(url) = url {
64        debug!("Downloading {release_type} from url: {url}");
65        let (upgrade_bin_path, version) = download_and_extract_release(
66            release_type,
67            Some(url),
68            None,
69            &*release_repo,
70            verbosity,
71            None,
72        )
73        .await?;
74        Ok((upgrade_bin_path, Version::parse(&version)?))
75    } else {
76        if verbosity != VerbosityLevel::Minimal {
77            println!("Retrieving latest version of {release_type}...");
78        }
79        debug!("Retrieving latest version of {release_type}...");
80        let latest_version = release_repo.get_latest_version(&release_type).await?;
81        if verbosity != VerbosityLevel::Minimal {
82            println!("Latest version is {latest_version}");
83        }
84        debug!("Download latest version {latest_version} of {release_type}");
85
86        let (upgrade_bin_path, _) = download_and_extract_release(
87            release_type,
88            None,
89            Some(latest_version.to_string()),
90            &*release_repo,
91            verbosity,
92            None,
93        )
94        .await?;
95        Ok((upgrade_bin_path, latest_version))
96    }
97}
98
99pub fn print_upgrade_summary(upgrade_summary: Vec<(String, UpgradeResult)>) {
100    println!("Upgrade summary:");
101    for (service_name, upgrade_result) in upgrade_summary {
102        match upgrade_result {
103            UpgradeResult::NotRequired => {
104                println!("- {service_name} did not require an upgrade");
105            }
106            UpgradeResult::Upgraded(previous_version, new_version) => {
107                println!(
108                    "{} {} upgraded from {previous_version} to {new_version}",
109                    "✓".green(),
110                    service_name
111                );
112            }
113            UpgradeResult::UpgradedButNotStarted(previous_version, new_version, _) => {
114                println!(
115                    "{} {} was upgraded from {previous_version} to {new_version} but it did not start",
116                    "✕".red(),
117                    service_name
118                );
119            }
120            UpgradeResult::Forced(previous_version, target_version) => {
121                println!(
122                    "{} Forced {} version change from {previous_version} to {target_version}.",
123                    "✓".green(),
124                    service_name
125                );
126            }
127            UpgradeResult::Error(msg) => {
128                println!("{} {} was not upgraded: {}", "✕".red(), service_name, msg);
129            }
130        }
131    }
132}
133
134pub async fn get_bin_path(
135    build: bool,
136    path: Option<PathBuf>,
137    release_type: ReleaseType,
138    version: Option<String>,
139    release_repo: &dyn AntReleaseRepoActions,
140    verbosity: VerbosityLevel,
141) -> Result<PathBuf> {
142    if build {
143        debug!("Obtaining bin path for {release_type:?} by building");
144        let target_dir = build_binary(&release_type)?;
145        Ok(target_dir.join(release_type.to_string()))
146    } else if let Some(path) = path {
147        debug!("Using the supplied custom binary for {release_type:?}: {path:?}");
148        Ok(path)
149    } else {
150        debug!("Downloading {release_type:?} binary with version {version:?}");
151        let (download_path, _) = download_and_extract_release(
152            release_type,
153            None,
154            version,
155            release_repo,
156            verbosity,
157            None,
158        )
159        .await?;
160        Ok(download_path)
161    }
162}
163
164// Returns the target dir after building the binary
165fn build_binary(bin_type: &ReleaseType) -> Result<PathBuf> {
166    debug!("Building {bin_type} binary");
167    let mut args = vec!["build", "--release"];
168    let bin_name = bin_type.to_string();
169    args.push("--bin");
170    args.push(&bin_name);
171
172    // Keep features consistent to avoid recompiling.
173    if cfg!(feature = "chaos") {
174        println!("*** Building testnet with CHAOS enabled. Watch out. ***");
175        args.push("--features");
176        args.push("chaos");
177    }
178    if cfg!(feature = "statemap") {
179        args.extend(["--features", "statemap"]);
180    }
181    if cfg!(feature = "otlp") {
182        args.extend(["--features", "otlp"]);
183    }
184    if cfg!(feature = "websockets") {
185        args.extend(["--features", "websockets"]);
186    }
187    if cfg!(feature = "open-metrics") {
188        args.extend(["--features", "open-metrics"]);
189    }
190
191    print_banner(&format!("Building {bin_name} binary"));
192
193    let mut target_dir = PathBuf::new();
194    let mut build_result = Command::new("cargo");
195    let _ = build_result.args(args.clone());
196
197    if let Ok(val) = std::env::var("CARGO_TARGET_DIR") {
198        let _ = build_result.env("CARGO_TARGET_DIR", val.clone());
199        target_dir.push(val);
200    } else {
201        target_dir.push("target");
202    }
203    let target_dir = target_dir.join("release");
204
205    let build_result = build_result
206        .stdout(Stdio::inherit())
207        .stderr(Stdio::inherit())
208        .output()?;
209
210    if !build_result.status.success() {
211        error!("Failed to build binaries {bin_name}");
212        return Err(eyre!("Failed to build binaries"));
213    }
214
215    Ok(target_dir)
216}