sn_node_manager/cmd/
faucet.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
9use super::{download_and_get_upgrade_bin_path, print_upgrade_summary};
10use crate::{
11    add_services::{add_faucet, config::AddFaucetServiceOptions},
12    config::{self, is_running_as_root},
13    helpers::{download_and_extract_release, get_bin_version},
14    print_banner, ServiceManager, VerbosityLevel,
15};
16use color_eyre::{eyre::eyre, Result};
17use colored::Colorize;
18use semver::Version;
19use sn_peers_acquisition::PeersArgs;
20use sn_releases::{ReleaseType, SafeReleaseRepoActions};
21use sn_service_management::{
22    control::{ServiceControl, ServiceController},
23    FaucetService, NodeRegistry, UpgradeOptions,
24};
25use sn_transfers::get_faucet_data_dir;
26use std::path::PathBuf;
27
28pub async fn add(
29    env_variables: Option<Vec<(String, String)>>,
30    log_dir_path: Option<PathBuf>,
31    peers_args: PeersArgs,
32    src_path: Option<PathBuf>,
33    url: Option<String>,
34    version: Option<String>,
35    verbosity: VerbosityLevel,
36) -> Result<()> {
37    if !is_running_as_root() {
38        error!("The faucet add command must run as the root user");
39        return Err(eyre!("The add command must run as the root user"));
40    }
41
42    if verbosity != VerbosityLevel::Minimal {
43        print_banner("Add Faucet Service");
44    }
45
46    let service_user = "safe";
47    let service_manager = ServiceController {};
48    service_manager.create_service_user(service_user)?;
49
50    let service_log_dir_path = config::get_service_log_dir_path(
51        ReleaseType::Faucet,
52        log_dir_path,
53        Some(service_user.to_string()),
54    )?;
55
56    let mut node_registry = NodeRegistry::load(&config::get_node_registry_path()?)?;
57    let release_repo = <dyn SafeReleaseRepoActions>::default_config();
58
59    let (faucet_src_bin_path, version) = if let Some(path) = src_path {
60        let version = get_bin_version(&path)?;
61        (path, version)
62    } else {
63        download_and_extract_release(
64            ReleaseType::Faucet,
65            url.clone(),
66            version,
67            &*release_repo,
68            verbosity,
69            None,
70        )
71        .await?
72    };
73
74    info!("Adding faucet service");
75    add_faucet(
76        AddFaucetServiceOptions {
77            bootstrap_peers: peers_args.get_peers().await?,
78            env_variables,
79            faucet_src_bin_path,
80            faucet_install_bin_path: PathBuf::from("/usr/local/bin/faucet"),
81            local: false,
82            service_data_dir_path: get_faucet_data_dir(),
83            service_log_dir_path,
84            user: service_user.to_string(),
85            version,
86        },
87        &mut node_registry,
88        &service_manager,
89        verbosity,
90    )?;
91
92    Ok(())
93}
94
95pub async fn start(verbosity: VerbosityLevel) -> Result<()> {
96    if !is_running_as_root() {
97        error!("The faucet start command must run as the root user");
98        return Err(eyre!("The start command must run as the root user"));
99    }
100
101    let mut node_registry = NodeRegistry::load(&config::get_node_registry_path()?)?;
102    if let Some(faucet) = &mut node_registry.faucet {
103        if verbosity != VerbosityLevel::Minimal {
104            print_banner("Start Faucet Service");
105        }
106        info!("Starting faucet service");
107
108        let service = FaucetService::new(faucet, Box::new(ServiceController {}));
109        let mut service_manager = ServiceManager::new(
110            service,
111            Box::new(ServiceController {}),
112            VerbosityLevel::Normal,
113        );
114        service_manager.start().await?;
115
116        node_registry.save()?;
117        return Ok(());
118    }
119
120    error!("The faucet service has not been added yet");
121    Err(eyre!("The faucet service has not been added yet"))
122}
123
124pub async fn stop(verbosity: VerbosityLevel) -> Result<()> {
125    if !is_running_as_root() {
126        error!("The faucet stop command must run as the root user");
127        return Err(eyre!("The stop command must run as the root user"));
128    }
129
130    let mut node_registry = NodeRegistry::load(&config::get_node_registry_path()?)?;
131    if let Some(faucet) = &mut node_registry.faucet {
132        if verbosity != VerbosityLevel::Minimal {
133            print_banner("Stop Faucet Service");
134        }
135        info!("Stopping faucet service");
136
137        let service = FaucetService::new(faucet, Box::new(ServiceController {}));
138        let mut service_manager =
139            ServiceManager::new(service, Box::new(ServiceController {}), verbosity);
140        service_manager.stop().await?;
141
142        node_registry.save()?;
143
144        return Ok(());
145    }
146
147    error!("The faucet service has not been added yet");
148    Err(eyre!("The faucet service has not been added yet"))
149}
150
151pub async fn upgrade(
152    do_not_start: bool,
153    force: bool,
154    provided_env_variables: Option<Vec<(String, String)>>,
155    url: Option<String>,
156    version: Option<String>,
157    verbosity: VerbosityLevel,
158) -> Result<()> {
159    if !is_running_as_root() {
160        return Err(eyre!("The upgrade command must run as the root user"));
161    }
162
163    let mut node_registry = NodeRegistry::load(&config::get_node_registry_path()?)?;
164    if node_registry.faucet.is_none() {
165        println!("No faucet service has been created yet. No upgrade required.");
166        return Ok(());
167    }
168
169    if verbosity != VerbosityLevel::Minimal {
170        print_banner("Upgrade Faucet Service");
171    }
172    info!("Upgrading faucet service");
173
174    let (upgrade_bin_path, target_version) =
175        download_and_get_upgrade_bin_path(None, ReleaseType::Faucet, url, version, verbosity)
176            .await?;
177    let faucet = node_registry.faucet.as_mut().unwrap();
178
179    if !force {
180        let current_version = Version::parse(&faucet.version)?;
181        if target_version <= current_version {
182            println!(
183                "{} The faucet is already at the latest version",
184                "✓".green()
185            );
186            return Ok(());
187        }
188    }
189
190    let env_variables = if provided_env_variables.is_some() {
191        &provided_env_variables
192    } else {
193        &node_registry.environment_variables
194    };
195    let options = UpgradeOptions {
196        auto_restart: true,
197        bootstrap_peers: node_registry.bootstrap_peers.clone(),
198        env_variables: env_variables.clone(),
199        force,
200        start_service: !do_not_start,
201        target_bin_path: upgrade_bin_path.clone(),
202        target_version: target_version.clone(),
203    };
204    let service = FaucetService::new(faucet, Box::new(ServiceController {}));
205    let mut service_manager =
206        ServiceManager::new(service, Box::new(ServiceController {}), verbosity);
207
208    match service_manager.upgrade(options).await {
209        Ok(upgrade_result) => {
210            print_upgrade_summary(vec![("faucet".to_string(), upgrade_result)]);
211            node_registry.save()?;
212            Ok(())
213        }
214        Err(e) => Err(eyre!("Upgrade failed: {e}")),
215    }
216}