From 1cf2b10e310d8cc4670fcaee58dda8fa51d8f4db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Wed, 6 Sep 2023 09:58:24 +0200 Subject: [PATCH] clients: handle config upgrade failure --- .../src/client/config/old_config_v1_1_20_2.rs | 15 +++++--- clients/native/src/commands/mod.rs | 6 +-- clients/socks5/src/commands/mod.rs | 6 +-- .../socks5/src/config/old_config_v1_1_20_2.rs | 16 +++++--- .../config/disk_persistence/old_v1_1_20_2.rs | 16 ++++---- common/client-core/src/error.rs | 3 ++ nym-connect/desktop/Cargo.lock | 2 +- .../desktop/src-tauri/src/config/mod.rs | 37 ++++++++++++++++++- .../src/config/old_config_v1_1_20_2.rs | 7 ++-- .../desktop/src-tauri/src/config/upgrade.rs | 9 +++-- nym-connect/desktop/src-tauri/src/error.rs | 7 ++++ .../network-requester/src/cli/mod.rs | 6 +-- .../src/config/old_config_v1_1_20_2.rs | 16 +++++--- 13 files changed, 104 insertions(+), 42 deletions(-) diff --git a/clients/native/src/client/config/old_config_v1_1_20_2.rs b/clients/native/src/client/config/old_config_v1_1_20_2.rs index 6f4686b063..30969b0f12 100644 --- a/clients/native/src/client/config/old_config_v1_1_20_2.rs +++ b/clients/native/src/client/config/old_config_v1_1_20_2.rs @@ -1,8 +1,13 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::client::config::persistence::ClientPaths; -use crate::client::config::{default_config_filepath, Config, Socket, SocketType}; +use crate::{ + client::config::{ + default_config_filepath, persistence::ClientPaths, Config, Socket, SocketType, + }, + error::ClientError, +}; + use nym_bin_common::logging::LoggingSettings; use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2; use nym_client_core::config::old_config_v1_1_20_2::ConfigV1_1_20_2 as BaseConfigV1_1_20_2; @@ -43,18 +48,18 @@ impl ConfigV1_1_20_2 { // in this upgrade, gateway endpoint configuration was moved out of the config file, // so its returned to be stored elsewhere. - pub fn upgrade(self) -> (Config, GatewayEndpointConfig) { + pub fn upgrade(self) -> Result<(Config, GatewayEndpointConfig), ClientError> { let gateway_details = self.base.client.gateway_endpoint.clone().into(); let config = Config { base: self.base.into(), socket: self.socket.into(), storage_paths: ClientPaths { - common_paths: self.storage_paths.common_paths.upgrade_default(), + common_paths: self.storage_paths.common_paths.upgrade_default()?, }, logging: self.logging, }; - (config, gateway_details) + Ok((config, gateway_details)) } } diff --git a/clients/native/src/commands/mod.rs b/clients/native/src/commands/mod.rs index 6badc58748..e1896bcfb6 100644 --- a/clients/native/src/commands/mod.rs +++ b/clients/native/src/commands/mod.rs @@ -157,7 +157,7 @@ fn try_upgrade_v1_1_13_config(id: &str) -> Result { let updated_step1: ConfigV1_1_20 = old_config.into(); let updated_step2: ConfigV1_1_20_2 = updated_step1.into(); - let (updated, gateway_config) = updated_step2.upgrade(); + let (updated, gateway_config) = updated_step2.upgrade()?; persist_gateway_details(&updated, gateway_config)?; updated.save_to_default_location()?; @@ -177,7 +177,7 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result { info!("It is going to get updated to the current specification."); let updated_step1: ConfigV1_1_20_2 = old_config.into(); - let (updated, gateway_config) = updated_step1.upgrade(); + let (updated, gateway_config) = updated_step1.upgrade()?; persist_gateway_details(&updated, gateway_config)?; updated.save_to_default_location()?; @@ -194,7 +194,7 @@ fn try_upgrade_v1_1_20_2_config(id: &str) -> Result { info!("It seems the client is using <= v1.1.20_2 config template."); info!("It is going to get updated to the current specification."); - let (updated, gateway_config) = old_config.upgrade(); + let (updated, gateway_config) = old_config.upgrade()?; persist_gateway_details(&updated, gateway_config)?; updated.save_to_default_location()?; diff --git a/clients/socks5/src/commands/mod.rs b/clients/socks5/src/commands/mod.rs index d2b37b5a84..f9e876015d 100644 --- a/clients/socks5/src/commands/mod.rs +++ b/clients/socks5/src/commands/mod.rs @@ -199,7 +199,7 @@ fn try_upgrade_v1_1_13_config(id: &str) -> Result { let updated_step1: ConfigV1_1_20 = old_config.into(); let updated_step2: ConfigV1_1_20_2 = updated_step1.into(); - let (updated, gateway_config) = updated_step2.upgrade(); + let (updated, gateway_config) = updated_step2.upgrade()?; persist_gateway_details(&updated, gateway_config)?; updated.save_to_default_location()?; @@ -219,7 +219,7 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result { info!("It is going to get updated to the current specification."); let updated_step1: ConfigV1_1_20_2 = old_config.into(); - let (updated, gateway_config) = updated_step1.upgrade(); + let (updated, gateway_config) = updated_step1.upgrade()?; persist_gateway_details(&updated, gateway_config)?; updated.save_to_default_location()?; @@ -236,7 +236,7 @@ fn try_upgrade_v1_1_20_2_config(id: &str) -> Result { info!("It seems the client is using <= v1.1.20_2 config template."); info!("It is going to get updated to the current specification."); - let (updated, gateway_config) = old_config.upgrade(); + let (updated, gateway_config) = old_config.upgrade()?; persist_gateway_details(&updated, gateway_config)?; updated.save_to_default_location()?; diff --git a/clients/socks5/src/config/old_config_v1_1_20_2.rs b/clients/socks5/src/config/old_config_v1_1_20_2.rs index 47d6dabe3f..998bd90b32 100644 --- a/clients/socks5/src/config/old_config_v1_1_20_2.rs +++ b/clients/socks5/src/config/old_config_v1_1_20_2.rs @@ -1,17 +1,21 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::config::persistence::SocksClientPaths; -use crate::config::{default_config_filepath, Config}; +use crate::{ + config::{default_config_filepath, persistence::SocksClientPaths, Config}, + error::Socks5ClientError, +}; + use nym_bin_common::logging::LoggingSettings; use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2; use nym_client_core::config::GatewayEndpointConfig; use nym_config::read_config_from_toml_file; -pub use nym_socks5_client_core::config::old_config_v1_1_20_2::ConfigV1_1_20_2 as CoreConfigV1_1_20_2; use serde::{Deserialize, Serialize}; use std::io; use std::path::Path; +pub use nym_socks5_client_core::config::old_config_v1_1_20_2::ConfigV1_1_20_2 as CoreConfigV1_1_20_2; + #[derive(Debug, Deserialize, PartialEq, Eq, Serialize, Clone)] pub struct SocksClientPathsV1_1_20_2 { #[serde(flatten)] @@ -39,16 +43,16 @@ impl ConfigV1_1_20_2 { // in this upgrade, gateway endpoint configuration was moved out of the config file, // so its returned to be stored elsewhere. - pub fn upgrade(self) -> (Config, GatewayEndpointConfig) { + pub fn upgrade(self) -> Result<(Config, GatewayEndpointConfig), Socks5ClientError> { let gateway_details = self.core.base.client.gateway_endpoint.clone().into(); let config = Config { core: self.core.into(), storage_paths: SocksClientPaths { - common_paths: self.storage_paths.common_paths.upgrade_default(), + common_paths: self.storage_paths.common_paths.upgrade_default()?, }, logging: self.logging, }; - (config, gateway_details) + Ok((config, gateway_details)) } } diff --git a/common/client-core/src/config/disk_persistence/old_v1_1_20_2.rs b/common/client-core/src/config/disk_persistence/old_v1_1_20_2.rs index 3346dca6ca..d26cf1461c 100644 --- a/common/client-core/src/config/disk_persistence/old_v1_1_20_2.rs +++ b/common/client-core/src/config/disk_persistence/old_v1_1_20_2.rs @@ -3,6 +3,7 @@ use crate::config::disk_persistence::keys_paths::ClientKeysPaths; use crate::config::disk_persistence::{CommonClientPaths, DEFAULT_GATEWAY_DETAILS_FILENAME}; +use crate::error::ClientCoreError; use serde::{Deserialize, Serialize}; use std::path::PathBuf; @@ -15,16 +16,17 @@ pub struct CommonClientPathsV1_1_20_2 { } impl CommonClientPathsV1_1_20_2 { - pub fn upgrade_default(self) -> CommonClientPaths { - let data_dir = self - .reply_surb_database - .parent() - .expect("client paths upgrade failure"); - CommonClientPaths { + pub fn upgrade_default(self) -> Result { + let data_dir = self.reply_surb_database.parent().ok_or_else(|| { + ClientCoreError::UnableToUpgradeConfigFile { + new_version: "1.1.20-2".to_string(), + } + })?; + Ok(CommonClientPaths { keys: self.keys, gateway_details: data_dir.join(DEFAULT_GATEWAY_DETAILS_FILENAME), credentials_database: self.credentials_database, reply_surb_database: self.reply_surb_database, - } + }) } } diff --git a/common/client-core/src/error.rs b/common/client-core/src/error.rs index c0dc76f1e6..0c30c149cb 100644 --- a/common/client-core/src/error.rs +++ b/common/client-core/src/error.rs @@ -119,6 +119,9 @@ pub enum ClientCoreError { #[error("the provided gateway details (for gateway {gateway_id}) do not correspond to the shared keys")] MismatchedGatewayDetails { gateway_id: String }, + + #[error("unable to upgrade config file to `{new_version}`")] + UnableToUpgradeConfigFile { new_version: String }, } /// Set of messages that the client can send to listeners via the task manager diff --git a/nym-connect/desktop/Cargo.lock b/nym-connect/desktop/Cargo.lock index e7c60df056..0181354c1d 100644 --- a/nym-connect/desktop/Cargo.lock +++ b/nym-connect/desktop/Cargo.lock @@ -3992,7 +3992,7 @@ dependencies = [ [[package]] name = "nym-connect" -version = "1.1.19" +version = "1.1.20" dependencies = [ "anyhow", "bip39", diff --git a/nym-connect/desktop/src-tauri/src/config/mod.rs b/nym-connect/desktop/src-tauri/src/config/mod.rs index 538b5e5fbb..40de5d4827 100644 --- a/nym-connect/desktop/src-tauri/src/config/mod.rs +++ b/nym-connect/desktop/src-tauri/src/config/mod.rs @@ -137,6 +137,20 @@ fn init_paths(id: &str) -> io::Result<()> { fs::create_dir_all(default_config_directory(id)) } +fn try_extract_version_for_upgrade_failure(err: BackendError) -> Option { + if let BackendError::ClientCoreError { source } = err { + if let nym_client_core::error::ClientCoreError::UnableToUpgradeConfigFile { new_version } = + source + { + Some(new_version) + } else { + None + } + } else { + None + } +} + pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: String) -> Result<()> { log::trace!("Initialising client..."); @@ -145,10 +159,29 @@ pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: Str let _validated = identity::PublicKey::from_base58_string(&chosen_gateway_id) .map_err(|_| BackendError::UnableToParseGateway)?; - let already_init = if default_config_filepath(&id).exists() { + let config_path = default_config_filepath(&id); + let already_init = if config_path.exists() { // in case we're using old config, try to upgrade it // (if we're using the current version, it's a no-op) - try_upgrade_config(&id)?; + if let Err(err) = try_upgrade_config(&id) { + log::error!( + "Failed to upgrade config file {}: {:?}", + config_path.display(), + err + ); + if let Some(failed_at_version) = try_extract_version_for_upgrade_failure(err) { + return Err( + BackendError::CouldNotUpgradeExistingConfigurationFileAtVersion { + file: config_path, + failed_at_version, + }, + ); + } else { + return Err(BackendError::CouldNotUpgradeExistingConfigurationFile { + file: config_path, + }); + } + }; eprintln!("SOCKS5 client \"{id}\" was already initialised before"); true } else { diff --git a/nym-connect/desktop/src-tauri/src/config/old_config_v1_1_20_2.rs b/nym-connect/desktop/src-tauri/src/config/old_config_v1_1_20_2.rs index df6356b226..0a2f017a75 100644 --- a/nym-connect/desktop/src-tauri/src/config/old_config_v1_1_20_2.rs +++ b/nym-connect/desktop/src-tauri/src/config/old_config_v1_1_20_2.rs @@ -3,6 +3,7 @@ use crate::config::persistence::NymConnectPaths; use crate::config::{default_config_filepath, Config}; +use crate::error::Result; use nym_bin_common::logging::LoggingSettings; use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2; use nym_client_core::config::GatewayEndpointConfig; @@ -39,16 +40,16 @@ impl ConfigV1_1_20_2 { // in this upgrade, gateway endpoint configuration was moved out of the config file, // so its returned to be stored elsewhere. - pub fn upgrade(self) -> (Config, GatewayEndpointConfig) { + pub fn upgrade(self) -> Result<(Config, GatewayEndpointConfig)> { let gateway_details = self.core.base.client.gateway_endpoint.clone().into(); let config = Config { core: self.core.into(), storage_paths: NymConnectPaths { - common_paths: self.storage_paths.common_paths.upgrade_default(), + common_paths: self.storage_paths.common_paths.upgrade_default()?, }, // logging: self.logging, }; - (config, gateway_details) + Ok((config, gateway_details)) } } diff --git a/nym-connect/desktop/src-tauri/src/config/upgrade.rs b/nym-connect/desktop/src-tauri/src/config/upgrade.rs index 2f4d37b777..2f41a964f4 100644 --- a/nym-connect/desktop/src-tauri/src/config/upgrade.rs +++ b/nym-connect/desktop/src-tauri/src/config/upgrade.rs @@ -8,7 +8,7 @@ use crate::{ }, error::{BackendError, Result}, }; -use log::info; +use log::{debug, info}; use nym_client_core::{ client::{ base_client::storage::gateway_details::{OnDiskGatewayDetails, PersistedGatewayDetails}, @@ -52,7 +52,7 @@ fn try_upgrade_v1_1_13_config(id: &str) -> Result { let updated_step1: ConfigV1_1_20 = old_config.into(); let updated_step2: ConfigV1_1_20_2 = updated_step1.into(); - let (updated, gateway_config) = updated_step2.upgrade(); + let (updated, gateway_config) = updated_step2.upgrade()?; persist_gateway_details(&updated, gateway_config)?; updated.save_to_default_location()?; @@ -72,7 +72,7 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result { info!("It is going to get updated to the current specification."); let updated_step1: ConfigV1_1_20_2 = old_config.into(); - let (updated, gateway_config) = updated_step1.upgrade(); + let (updated, gateway_config) = updated_step1.upgrade()?; persist_gateway_details(&updated, gateway_config)?; updated.save_to_default_location()?; @@ -89,7 +89,7 @@ fn try_upgrade_v1_1_20_2_config(id: &str) -> Result { info!("It seems the client is using <= v1.1.20_2 config template."); info!("It is going to get updated to the current specification."); - let (updated, gateway_config) = old_config.upgrade(); + let (updated, gateway_config) = old_config.upgrade()?; persist_gateway_details(&updated, gateway_config)?; updated.save_to_default_location()?; @@ -97,6 +97,7 @@ fn try_upgrade_v1_1_20_2_config(id: &str) -> Result { } pub fn try_upgrade_config(id: &str) -> Result<()> { + debug!("Attempting to upgrade config file for \"{id}\""); if try_upgrade_v1_1_13_config(id)? { return Ok(()); } diff --git a/nym-connect/desktop/src-tauri/src/error.rs b/nym-connect/desktop/src-tauri/src/error.rs index a6290280c3..5b324a8850 100644 --- a/nym-connect/desktop/src-tauri/src/error.rs +++ b/nym-connect/desktop/src-tauri/src/error.rs @@ -81,6 +81,13 @@ pub enum BackendError { CouldNotGetConfigFilename, #[error("could not load existing gateway configuration")] CouldNotLoadExistingGatewayConfiguration(std::io::Error), + #[error("could not upgrade `{file}` to latest version")] + CouldNotUpgradeExistingConfigurationFile { file: std::path::PathBuf }, + #[error("could not upgrade `{file}` to latest version (failed at {failed_at_version})")] + CouldNotUpgradeExistingConfigurationFileAtVersion { + file: std::path::PathBuf, + failed_at_version: String, + }, #[error("no gateways found in directory")] NoGatewaysFoundInDirectory, diff --git a/service-providers/network-requester/src/cli/mod.rs b/service-providers/network-requester/src/cli/mod.rs index 98106b3ca7..3df493c724 100644 --- a/service-providers/network-requester/src/cli/mod.rs +++ b/service-providers/network-requester/src/cli/mod.rs @@ -179,7 +179,7 @@ fn try_upgrade_v1_1_13_config(id: &str) -> Result { let updated_step1: ConfigV1_1_20 = old_config.into(); let updated_step2: ConfigV1_1_20_2 = updated_step1.into(); - let (updated, gateway_config) = updated_step2.upgrade(); + let (updated, gateway_config) = updated_step2.upgrade()?; persist_gateway_details(&updated, gateway_config)?; updated.save_to_default_location()?; @@ -201,7 +201,7 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result { info!("It is going to get updated to the current specification."); let updated_step1: ConfigV1_1_20_2 = old_config.into(); - let (updated, gateway_config) = updated_step1.upgrade(); + let (updated, gateway_config) = updated_step1.upgrade()?; persist_gateway_details(&updated, gateway_config)?; updated.save_to_default_location()?; @@ -220,7 +220,7 @@ fn try_upgrade_v1_1_20_2_config(id: &str) -> Result info!("It seems the client is using <= v1.1.20_2 config template."); info!("It is going to get updated to the current specification."); - let (updated, gateway_config) = old_config.upgrade(); + let (updated, gateway_config) = old_config.upgrade()?; persist_gateway_details(&updated, gateway_config)?; updated.save_to_default_location()?; diff --git a/service-providers/network-requester/src/config/old_config_v1_1_20_2.rs b/service-providers/network-requester/src/config/old_config_v1_1_20_2.rs index 280d6e1ec1..68cd8031ec 100644 --- a/service-providers/network-requester/src/config/old_config_v1_1_20_2.rs +++ b/service-providers/network-requester/src/config/old_config_v1_1_20_2.rs @@ -1,8 +1,14 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use crate::config::persistence::NetworkRequesterPaths; -use crate::config::{default_config_filepath, Config, Debug, NetworkRequester}; +use crate::{ + config::{ + default_config_filepath, persistence::NetworkRequesterPaths, Config, Debug, + NetworkRequester, + }, + error::NetworkRequesterError, +}; + use log::trace; use nym_bin_common::logging::LoggingSettings; use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2; @@ -58,7 +64,7 @@ impl ConfigV1_1_20_2 { // in this upgrade, gateway endpoint configuration was moved out of the config file, // so its returned to be stored elsewhere. - pub fn upgrade(self) -> (Config, GatewayEndpointConfig) { + pub fn upgrade(self) -> Result<(Config, GatewayEndpointConfig), NetworkRequesterError> { trace!("Upgrading from v1.1.20_2"); let gateway_details = self.base.client.gateway_endpoint.clone().into(); let nr_description = self @@ -72,7 +78,7 @@ impl ConfigV1_1_20_2 { let config = Config { base: self.base.into(), storage_paths: NetworkRequesterPaths { - common_paths: self.storage_paths.common_paths.upgrade_default(), + common_paths: self.storage_paths.common_paths.upgrade_default()?, allowed_list_location: self.storage_paths.allowed_list_location, unknown_list_location: self.storage_paths.unknown_list_location, nr_description, @@ -82,7 +88,7 @@ impl ConfigV1_1_20_2 { network_requester: self.network_requester.into(), }; - (config, gateway_details) + Ok((config, gateway_details)) } }