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