Feature/upgrade command (#381)
* Exposed version parsing from version checker * Ability to upgrade mixnode from 0.8.X to 0.9.0 * Ability to upgrade gateway from 0.8.X to 0.9.0 * Ability to upgrade native client from 0.8.X to 0.9.0 * Ability to upgrade socks5 client from 0.8.X to 0.9.0 * Typo * Minor renaming * Preventing upgrade if current is not a release version * Additional upgrade restrictions * Corrected version bound
This commit is contained in:
committed by
GitHub
parent
e7bd27a2d0
commit
c8bf454ccc
Generated
+4
@@ -1550,6 +1550,7 @@ dependencies = [
|
||||
"tokio-tungstenite",
|
||||
"topology",
|
||||
"url",
|
||||
"version-checker",
|
||||
"websocket-requests",
|
||||
]
|
||||
|
||||
@@ -1601,6 +1602,7 @@ dependencies = [
|
||||
"tokio-tungstenite",
|
||||
"tokio-util",
|
||||
"tungstenite",
|
||||
"version-checker",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1627,6 +1629,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"topology",
|
||||
"version-checker",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1656,6 +1659,7 @@ dependencies = [
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"topology",
|
||||
"version-checker",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -21,6 +21,8 @@ use std::time::Duration;
|
||||
|
||||
pub mod persistence;
|
||||
|
||||
pub const MISSING_VALUE: &str = "MISSING VALUE";
|
||||
|
||||
// 'CLIENT'
|
||||
const DEFAULT_DIRECTORY_SERVER: &str = "https://directory.nymtech.net";
|
||||
// 'DEBUG'
|
||||
@@ -39,6 +41,10 @@ const DEFAULT_VPN_KEY_REUSE_LIMIT: usize = 1000;
|
||||
|
||||
const ZERO_DELAY: Duration = Duration::from_nanos(0);
|
||||
|
||||
pub fn missing_string_value() -> String {
|
||||
MISSING_VALUE.to_string()
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Config<T> {
|
||||
@@ -140,6 +146,10 @@ impl<T: NymConfig> Config<T> {
|
||||
self.debug.vpn_key_reuse_limit = Some(reuse_limit)
|
||||
}
|
||||
|
||||
pub fn set_custom_version(&mut self, version: &str) {
|
||||
self.client.version = version.to_string();
|
||||
}
|
||||
|
||||
pub fn get_id(&self) -> String {
|
||||
self.client.id.clone()
|
||||
}
|
||||
@@ -251,6 +261,10 @@ impl<T: NymConfig> Config<T> {
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_version(&self) -> &str {
|
||||
&self.client.version
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NymConfig> Default for Config<T> {
|
||||
@@ -267,6 +281,7 @@ impl<T: NymConfig> Default for Config<T> {
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Client<T> {
|
||||
/// Version of the client for which this configuration was created.
|
||||
#[serde(default = "missing_string_value")]
|
||||
version: String,
|
||||
|
||||
/// ID specifies the human readable ID of this particular client.
|
||||
@@ -278,6 +293,7 @@ pub struct Client<T> {
|
||||
/// Special mode of the system such that all messages are sent as soon as they are received
|
||||
/// and no cover traffic is generated. If set all message delays are set to 0 and overwriting
|
||||
/// 'Debug' values will have no effect.
|
||||
#[serde(default)]
|
||||
vpn_mode: bool,
|
||||
|
||||
/// Path to file containing private identity key.
|
||||
|
||||
@@ -40,6 +40,7 @@ nymsphinx = { path = "../../common/nymsphinx" }
|
||||
pemstore = { path = "../../common/pemstore" }
|
||||
topology = { path = "../../common/topology" }
|
||||
websocket-requests = { path = "websocket-requests" }
|
||||
version-checker = { path = "../../common/version-checker" }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.1.0"
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
use crate::client::config::template::config_template;
|
||||
use client_core::config::Config as BaseConfig;
|
||||
pub use client_core::config::MISSING_VALUE;
|
||||
use config::NymConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
use crate::client::config::{Config, SocketType};
|
||||
use clap::ArgMatches;
|
||||
|
||||
pub mod init;
|
||||
pub mod run;
|
||||
pub(crate) mod init;
|
||||
pub(crate) mod run;
|
||||
pub(crate) mod upgrade;
|
||||
|
||||
pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config {
|
||||
if let Some(directory) = matches.value_of("directory") {
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
// Copyright 2020 Nym Technologies SA
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::client::config::{Config, MISSING_VALUE};
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use config::NymConfig;
|
||||
use std::fmt::Display;
|
||||
use std::process;
|
||||
use version_checker::{parse_version, Version};
|
||||
|
||||
fn print_start_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||
println!(
|
||||
"\n==================\nTrying to upgrade mixnode from {} to {} ...",
|
||||
from, to
|
||||
);
|
||||
}
|
||||
|
||||
fn print_failed_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||
eprintln!(
|
||||
"Upgrade from {} to {} failed!\n==================\n",
|
||||
from, to
|
||||
);
|
||||
}
|
||||
|
||||
fn print_successful_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||
println!(
|
||||
"Upgrade from {} to {} was successful!\n==================\n",
|
||||
from, to
|
||||
);
|
||||
}
|
||||
|
||||
fn pre_090_upgrade(from: &str, mut config: Config) -> Config {
|
||||
// this is not extracted to separate function as you only have to manually pass version
|
||||
// if upgrading from pre090 version
|
||||
let from = match from.strip_prefix("v") {
|
||||
Some(stripped) => stripped,
|
||||
None => from,
|
||||
};
|
||||
|
||||
let from = match from.strip_prefix("V") {
|
||||
Some(stripped) => stripped,
|
||||
None => from,
|
||||
};
|
||||
|
||||
let from_version = parse_version(from).expect("invalid version provided!");
|
||||
if from_version.major == 0 && from_version.minor < 8 {
|
||||
// technically this could be implemented, but is there any point in that?
|
||||
eprintln!("upgrading node from before v0.8.0 is not supported. Please run `init` with new binary instead");
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
if (from_version.major == 0 && from_version.minor >= 9) || from_version.major >= 1 {
|
||||
eprintln!("self reported version is higher than 0.9.0. Those releases should have already contained version numbers in config! Make sure you provided correct version");
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
// note: current is guaranteed to not have any `build` information suffix (nor pre-release
|
||||
// information), as this was asserted at the beginning of this command)
|
||||
//
|
||||
// upgrade to current (if it's a 0.9.X) or try to upgrade to 0.9.0 as an intermediate
|
||||
// step in future upgrades (so, for example, we might go 0.8.0 -> 0.9.0 -> 0.10.0)
|
||||
// this way we don't need to have all the crazy paths on how to upgrade from any version to any
|
||||
// other version. We just upgrade one minor version at a time.
|
||||
let current = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
|
||||
let to_version = if current.major == 0 && current.minor == 9 {
|
||||
current
|
||||
} else {
|
||||
Version::new(0, 9, 0)
|
||||
};
|
||||
|
||||
print_start_upgrade(&from_version, &to_version);
|
||||
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_version(to_version.to_string().as_ref());
|
||||
|
||||
config.save_to_file(None).unwrap_or_else(|err| {
|
||||
eprintln!("failed to overwrite config file! - {:?}", err);
|
||||
print_failed_upgrade(&from_version, &to_version);
|
||||
process::exit(1);
|
||||
});
|
||||
|
||||
print_successful_upgrade(from_version, to_version);
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
pub fn command_args<'a, 'b>() -> App<'a, 'b> {
|
||||
App::new("upgrade").about("Try to upgrade the mixnode")
|
||||
.arg(
|
||||
Arg::with_name("id")
|
||||
.long("id")
|
||||
.help("Id of the nym-client we want to upgrade")
|
||||
.takes_value(true)
|
||||
.required(true),
|
||||
)
|
||||
// the rest of arguments depend on the upgrade path
|
||||
.arg(Arg::with_name("current version")
|
||||
.long("current-version")
|
||||
.help("REQUIRED FOR PRE-0.9.0 UPGRADES. Self provided version of the nym-client if none is available in the config. NOTE: if provided incorrectly, results may be catastrophic.")
|
||||
.takes_value(true)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn execute(matches: &ArgMatches) {
|
||||
let current = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
|
||||
|
||||
// technically this is not a correct way of checking it as a released version might contain valid build identifiers
|
||||
// however, we are not using them ourselves at the moment and hence it should be fine.
|
||||
// if we change our mind, we could easily tweak this code
|
||||
if current.is_prerelease() || !current.build.is_empty() {
|
||||
eprintln!(
|
||||
"Trying to upgrade to a non-released version {}. This is not supported!",
|
||||
current
|
||||
);
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
let id = matches.value_of("id").unwrap();
|
||||
|
||||
let mut existing_config = Config::load_from_file(None, Some(id)).unwrap_or_else(|err| {
|
||||
eprintln!("failed to load existing config file! - {:?}", err);
|
||||
process::exit(1)
|
||||
});
|
||||
|
||||
// versions fields were added in 0.9.0
|
||||
if existing_config.get_base().get_version() == MISSING_VALUE {
|
||||
let self_reported_version = matches.value_of("current version").unwrap_or_else(|| {
|
||||
eprintln!(
|
||||
"trying to upgrade from pre v0.9.0 without providing current system version!"
|
||||
);
|
||||
process::exit(1)
|
||||
});
|
||||
|
||||
// upgrades up to 0.9.0
|
||||
existing_config = pre_090_upgrade(self_reported_version, existing_config);
|
||||
}
|
||||
|
||||
let config_version =
|
||||
Version::parse(existing_config.get_base().get_version()).unwrap_or_else(|err| {
|
||||
eprintln!("failed to parse node version! - {:?}", err);
|
||||
process::exit(1)
|
||||
});
|
||||
|
||||
if config_version.is_prerelease() || !config_version.build.is_empty() {
|
||||
eprintln!(
|
||||
"Trying to upgrade to from non-released version {}. This is not supported!",
|
||||
current
|
||||
);
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
// here be upgrade path to 0.10.0 and beyond based on version number from config
|
||||
if config_version == current {
|
||||
println!("You're using the most recent version!");
|
||||
} else {
|
||||
eprintln!("Cannot perform upgrade from {} to {}. Please let the developers know about this issue!", config_version, current);
|
||||
process::exit(1)
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ fn main() {
|
||||
.about("Implementation of the Nym Client")
|
||||
.subcommand(commands::init::command_args())
|
||||
.subcommand(commands::run::command_args())
|
||||
.subcommand(commands::upgrade::command_args())
|
||||
.get_matches();
|
||||
|
||||
execute(arg_matches);
|
||||
@@ -38,6 +39,7 @@ fn execute(matches: ArgMatches) {
|
||||
match matches.subcommand() {
|
||||
("init", Some(m)) => commands::init::execute(m),
|
||||
("run", Some(m)) => commands::run::execute(m),
|
||||
("upgrade", Some(m)) => commands::upgrade::execute(m),
|
||||
_ => println!("{}", usage()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ ordered-buffer = {path = "../../common/socks5/ordered-buffer"}
|
||||
socks5-requests = { path = "../../common/socks5/requests" }
|
||||
topology = { path = "../../common/topology" }
|
||||
proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
|
||||
version-checker = { path = "../../common/version-checker" }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.1.0"
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
use crate::client::config::template::config_template;
|
||||
use client_core::config::Config as BaseConfig;
|
||||
pub use client_core::config::MISSING_VALUE;
|
||||
use config::NymConfig;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
use crate::client::config::Config;
|
||||
use clap::ArgMatches;
|
||||
|
||||
pub mod init;
|
||||
pub mod run;
|
||||
pub(crate) mod init;
|
||||
pub(crate) mod run;
|
||||
pub(crate) mod upgrade;
|
||||
|
||||
pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config {
|
||||
if let Some(directory) = matches.value_of("directory") {
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
// Copyright 2020 Nym Technologies SA
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::client::config::{Config, MISSING_VALUE};
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use config::NymConfig;
|
||||
use std::fmt::Display;
|
||||
use std::process;
|
||||
use version_checker::{parse_version, Version};
|
||||
|
||||
fn print_start_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||
println!(
|
||||
"\n==================\nTrying to upgrade mixnode from {} to {} ...",
|
||||
from, to
|
||||
);
|
||||
}
|
||||
|
||||
fn print_failed_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||
eprintln!(
|
||||
"Upgrade from {} to {} failed!\n==================\n",
|
||||
from, to
|
||||
);
|
||||
}
|
||||
|
||||
fn print_successful_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||
println!(
|
||||
"Upgrade from {} to {} was successful!\n==================\n",
|
||||
from, to
|
||||
);
|
||||
}
|
||||
|
||||
fn pre_090_upgrade(from: &str, mut config: Config) -> Config {
|
||||
// this is not extracted to separate function as you only have to manually pass version
|
||||
// if upgrading from pre090 version
|
||||
let from = match from.strip_prefix("v") {
|
||||
Some(stripped) => stripped,
|
||||
None => from,
|
||||
};
|
||||
|
||||
let from = match from.strip_prefix("V") {
|
||||
Some(stripped) => stripped,
|
||||
None => from,
|
||||
};
|
||||
|
||||
let from_version = parse_version(from).expect("invalid version provided!");
|
||||
if from_version.major == 0 && from_version.minor < 8 {
|
||||
// technically this could be implemented, but is there any point in that?
|
||||
eprintln!("upgrading node from before v0.8.0 is not supported. Please run `init` with new binary instead");
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
if (from_version.major == 0 && from_version.minor >= 9) || from_version.major >= 1 {
|
||||
eprintln!("self reported version is higher than 0.9.0. Those releases should have already contained version numbers in config! Make sure you provided correct version");
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
// note: current is guaranteed to not have any `build` information suffix (nor pre-release
|
||||
// information), as this was asserted at the beginning of this command)
|
||||
//
|
||||
// upgrade to current (if it's a 0.9.X) or try to upgrade to 0.9.0 as an intermediate
|
||||
// step in future upgrades (so, for example, we might go 0.8.0 -> 0.9.0 -> 0.10.0)
|
||||
// this way we don't need to have all the crazy paths on how to upgrade from any version to any
|
||||
// other version. We just upgrade one minor version at a time.
|
||||
let current = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
|
||||
let to_version = if current.major == 0 && current.minor == 9 {
|
||||
current
|
||||
} else {
|
||||
Version::new(0, 9, 0)
|
||||
};
|
||||
|
||||
print_start_upgrade(&from_version, &to_version);
|
||||
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_version(to_version.to_string().as_ref());
|
||||
|
||||
config.save_to_file(None).unwrap_or_else(|err| {
|
||||
eprintln!("failed to overwrite config file! - {:?}", err);
|
||||
print_failed_upgrade(&from_version, &to_version);
|
||||
process::exit(1);
|
||||
});
|
||||
|
||||
print_successful_upgrade(from_version, to_version);
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
pub fn command_args<'a, 'b>() -> App<'a, 'b> {
|
||||
App::new("upgrade").about("Try to upgrade the mixnode")
|
||||
.arg(
|
||||
Arg::with_name("id")
|
||||
.long("id")
|
||||
.help("Id of the nym-socks5-client we want to upgrade")
|
||||
.takes_value(true)
|
||||
.required(true),
|
||||
)
|
||||
// the rest of arguments depend on the upgrade path
|
||||
.arg(Arg::with_name("current version")
|
||||
.long("current-version")
|
||||
.help("REQUIRED FOR PRE-0.9.0 UPGRADES. Self provided version of the nym-socks5-client if none is available in the config. NOTE: if provided incorrectly, results may be catastrophic.")
|
||||
.takes_value(true)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn execute(matches: &ArgMatches) {
|
||||
let current = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
|
||||
|
||||
// technically this is not a correct way of checking it as a released version might contain valid build identifiers
|
||||
// however, we are not using them ourselves at the moment and hence it should be fine.
|
||||
// if we change our mind, we could easily tweak this code
|
||||
if current.is_prerelease() || !current.build.is_empty() {
|
||||
eprintln!(
|
||||
"Trying to upgrade to a non-released version {}. This is not supported!",
|
||||
current
|
||||
);
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
let id = matches.value_of("id").unwrap();
|
||||
|
||||
let mut existing_config = Config::load_from_file(None, Some(id)).unwrap_or_else(|err| {
|
||||
eprintln!("failed to load existing config file! - {:?}", err);
|
||||
process::exit(1)
|
||||
});
|
||||
|
||||
// versions fields were added in 0.9.0
|
||||
if existing_config.get_base().get_version() == MISSING_VALUE {
|
||||
let self_reported_version = matches.value_of("current version").unwrap_or_else(|| {
|
||||
eprintln!(
|
||||
"trying to upgrade from pre v0.9.0 without providing current system version!"
|
||||
);
|
||||
process::exit(1)
|
||||
});
|
||||
|
||||
// upgrades up to 0.9.0
|
||||
existing_config = pre_090_upgrade(self_reported_version, existing_config);
|
||||
}
|
||||
|
||||
let config_version =
|
||||
Version::parse(existing_config.get_base().get_version()).unwrap_or_else(|err| {
|
||||
eprintln!("failed to parse node version! - {:?}", err);
|
||||
process::exit(1)
|
||||
});
|
||||
|
||||
if config_version.is_prerelease() || !config_version.build.is_empty() {
|
||||
eprintln!(
|
||||
"Trying to upgrade to from non-released version {}. This is not supported!",
|
||||
current
|
||||
);
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
// here be upgrade path to 0.10.0 and beyond based on version number from config
|
||||
if config_version == current {
|
||||
println!("You're using the most recent version!");
|
||||
} else {
|
||||
eprintln!("Cannot perform upgrade from {} to {}. Please let the developers know about this issue!", config_version, current);
|
||||
process::exit(1)
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ fn main() {
|
||||
.about("A Socks5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address")
|
||||
.subcommand(commands::init::command_args())
|
||||
.subcommand(commands::run::command_args())
|
||||
.subcommand(commands::upgrade::command_args())
|
||||
.get_matches();
|
||||
|
||||
execute(arg_matches);
|
||||
@@ -38,6 +39,7 @@ fn execute(matches: ArgMatches) {
|
||||
match matches.subcommand() {
|
||||
("init", Some(m)) => commands::init::execute(m),
|
||||
("run", Some(m)) => commands::run::execute(m),
|
||||
("upgrade", Some(m)) => commands::upgrade::execute(m),
|
||||
_ => println!("{}", usage()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use semver::Version;
|
||||
use semver::SemVerError;
|
||||
pub use semver::Version;
|
||||
|
||||
/// Checks whether given `version` is compatible with a given semantic version requirement `req`
|
||||
/// according to major-minor semver rules. The semantic version requirement can be passed as a full,
|
||||
@@ -32,6 +33,10 @@ pub fn is_minor_version_compatible(version: &str, req: &str) -> bool {
|
||||
expected_version.major == req_version.major && expected_version.minor == req_version.minor
|
||||
}
|
||||
|
||||
pub fn parse_version(raw_version: &str) -> Result<Version, SemVerError> {
|
||||
Version::parse(raw_version)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -31,6 +31,7 @@ mixnet-client = { path = "../common/client-libs/mixnet-client" }
|
||||
mixnode-common = { path = "../common/mixnode-common" }
|
||||
nymsphinx = { path = "../common/nymsphinx" }
|
||||
pemstore = { path = "../common/pemstore" }
|
||||
version-checker = { path = "../common/version-checker" }
|
||||
|
||||
[dependencies.tungstenite]
|
||||
version = "0.11"
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
use crate::config::Config;
|
||||
use clap::ArgMatches;
|
||||
|
||||
pub mod init;
|
||||
pub mod run;
|
||||
pub(crate) mod init;
|
||||
pub(crate) mod run;
|
||||
pub(crate) mod upgrade;
|
||||
|
||||
pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config {
|
||||
let mut was_mix_host_overridden = false;
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
// Copyright 2020 Nym Technologies SA
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::config::{Config, MISSING_VALUE};
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use config::NymConfig;
|
||||
use std::fmt::Display;
|
||||
use std::process;
|
||||
use version_checker::{parse_version, Version};
|
||||
|
||||
fn print_start_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||
println!(
|
||||
"\n==================\nTrying to upgrade gateway from {} to {} ...",
|
||||
from, to
|
||||
);
|
||||
}
|
||||
|
||||
fn print_failed_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||
eprintln!(
|
||||
"Upgrade from {} to {} failed!\n==================\n",
|
||||
from, to
|
||||
);
|
||||
}
|
||||
|
||||
fn print_successful_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||
println!(
|
||||
"Upgrade from {} to {} was successful!\n==================\n",
|
||||
from, to
|
||||
);
|
||||
}
|
||||
|
||||
fn pre_090_upgrade(from: &str, config: Config) -> Config {
|
||||
// this is not extracted to separate function as you only have to manually pass version
|
||||
// if upgrading from pre090 version
|
||||
let from = match from.strip_prefix("v") {
|
||||
Some(stripped) => stripped,
|
||||
None => from,
|
||||
};
|
||||
|
||||
let from = match from.strip_prefix("V") {
|
||||
Some(stripped) => stripped,
|
||||
None => from,
|
||||
};
|
||||
|
||||
let from_version = parse_version(from).expect("invalid version provided!");
|
||||
if from_version.major == 0 && from_version.minor < 8 {
|
||||
// technically this could be implemented, but is there any point in that?
|
||||
eprintln!("upgrading node from before v0.8.0 is not supported. Please run `init` with new binary instead");
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
if (from_version.major == 0 && from_version.minor >= 9) || from_version.major >= 1 {
|
||||
eprintln!("self reported version is higher than 0.9.0. Those releases should have already contained version numbers in config! Make sure you provided correct version");
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
// note: current is guaranteed to not have any `build` information suffix (nor pre-release
|
||||
// information), as this was asserted at the beginning of this command)
|
||||
//
|
||||
// upgrade to current (if it's a 0.9.X) or try to upgrade to 0.9.0 as an intermediate
|
||||
// step in future upgrades (so, for example, we might go 0.8.0 -> 0.9.0 -> 0.10.0)
|
||||
// this way we don't need to have all the crazy paths on how to upgrade from any version to any
|
||||
// other version. We just upgrade one minor version at a time.
|
||||
let current = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
|
||||
let to_version = if current.major == 0 && current.minor == 9 {
|
||||
current
|
||||
} else {
|
||||
Version::new(0, 9, 0)
|
||||
};
|
||||
|
||||
print_start_upgrade(&from_version, &to_version);
|
||||
|
||||
let upgraded_config = config.with_custom_version(to_version.to_string().as_ref());
|
||||
// TODO: THIS IS INCOMPLETE AS ONCE PRESENCE IS REMOVED IN 0.9.0 IT WILL ALSO NEED
|
||||
// TO BE PURGED FROM CONFIG
|
||||
|
||||
upgraded_config.save_to_file(None).unwrap_or_else(|err| {
|
||||
eprintln!("failed to overwrite config file! - {:?}", err);
|
||||
print_failed_upgrade(&from_version, &to_version);
|
||||
process::exit(1);
|
||||
});
|
||||
|
||||
print_successful_upgrade(from_version, to_version);
|
||||
|
||||
upgraded_config
|
||||
}
|
||||
|
||||
pub fn command_args<'a, 'b>() -> App<'a, 'b> {
|
||||
App::new("upgrade").about("Try to upgrade the gateway")
|
||||
.arg(
|
||||
Arg::with_name("id")
|
||||
.long("id")
|
||||
.help("Id of the nym-gateway we want to upgrade")
|
||||
.takes_value(true)
|
||||
.required(true),
|
||||
)
|
||||
// the rest of arguments depend on the upgrade path
|
||||
.arg(Arg::with_name("current version")
|
||||
.long("current-version")
|
||||
.help("REQUIRED FOR PRE-0.9.0 UPGRADES. Self provided version of the nym-gateway if none is available in the config. NOTE: if provided incorrectly, results may be catastrophic.")
|
||||
.takes_value(true)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn execute(matches: &ArgMatches) {
|
||||
let current = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
|
||||
|
||||
// technically this is not a correct way of checking it as a released version might contain valid build identifiers
|
||||
// however, we are not using them ourselves at the moment and hence it should be fine.
|
||||
// if we change our mind, we could easily tweak this code
|
||||
if current.is_prerelease() || !current.build.is_empty() {
|
||||
eprintln!(
|
||||
"Trying to upgrade to a non-released version {}. This is not supported!",
|
||||
current
|
||||
);
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
let id = matches.value_of("id").unwrap();
|
||||
|
||||
let mut existing_config = Config::load_from_file(None, Some(id)).unwrap_or_else(|err| {
|
||||
eprintln!("failed to load existing config file! - {:?}", err);
|
||||
process::exit(1)
|
||||
});
|
||||
|
||||
// versions fields were added in 0.9.0
|
||||
if existing_config.get_version() == MISSING_VALUE {
|
||||
let self_reported_version = matches.value_of("current version").unwrap_or_else(|| {
|
||||
eprintln!(
|
||||
"trying to upgrade from pre v0.9.0 without providing current system version!"
|
||||
);
|
||||
process::exit(1)
|
||||
});
|
||||
|
||||
// upgrades up to 0.9.0
|
||||
existing_config = pre_090_upgrade(self_reported_version, existing_config);
|
||||
}
|
||||
|
||||
let config_version = Version::parse(existing_config.get_version()).unwrap_or_else(|err| {
|
||||
eprintln!("failed to parse node version! - {:?}", err);
|
||||
process::exit(1)
|
||||
});
|
||||
|
||||
if config_version.is_prerelease() || !config_version.build.is_empty() {
|
||||
eprintln!(
|
||||
"Trying to upgrade to from non-released version {}. This is not supported!",
|
||||
current
|
||||
);
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
// here be upgrade path to 0.10.0 and beyond based on version number from config
|
||||
if config_version == current {
|
||||
println!("You're using the most recent version!");
|
||||
} else {
|
||||
eprintln!("Cannot perform upgrade from {} to {}. Please let the developers know about this issue!", config_version, current);
|
||||
process::exit(1)
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,8 @@ use std::time;
|
||||
pub mod persistence;
|
||||
mod template;
|
||||
|
||||
pub(crate) const MISSING_VALUE: &str = "MISSING VALUE";
|
||||
|
||||
// 'GATEWAY'
|
||||
const DEFAULT_MIX_LISTENING_PORT: u16 = 1789;
|
||||
const DEFAULT_CLIENT_LISTENING_PORT: u16 = 9000;
|
||||
@@ -90,6 +92,10 @@ impl NymConfig for Config {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn missing_string_value() -> String {
|
||||
MISSING_VALUE.to_string()
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new<S: Into<String>>(id: S) -> Self {
|
||||
Config::default().with_id(id)
|
||||
@@ -322,6 +328,11 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_custom_version(mut self, version: &str) -> Self {
|
||||
self.gateway.version = version.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
// getters
|
||||
pub fn get_config_file_save_location(&self) -> PathBuf {
|
||||
self.config_directory().join(Self::config_file_name())
|
||||
@@ -402,12 +413,17 @@ impl Config {
|
||||
pub fn get_cache_entry_ttl(&self) -> time::Duration {
|
||||
time::Duration::from_millis(self.debug.cache_entry_ttl)
|
||||
}
|
||||
|
||||
pub fn get_version(&self) -> &str {
|
||||
&self.gateway.version
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Gateway {
|
||||
/// Version of the gateway for which this configuration was created.
|
||||
#[serde(default = "missing_string_value")]
|
||||
version: String,
|
||||
|
||||
/// ID specifies the human readable ID of this particular gateway.
|
||||
|
||||
@@ -29,6 +29,7 @@ fn main() {
|
||||
.about("Implementation of the Nym Mixnet Gateway")
|
||||
.subcommand(commands::init::command_args())
|
||||
.subcommand(commands::run::command_args())
|
||||
.subcommand(commands::upgrade::command_args())
|
||||
.get_matches();
|
||||
|
||||
execute(arg_matches);
|
||||
@@ -38,6 +39,7 @@ fn execute(matches: ArgMatches) {
|
||||
match matches.subcommand() {
|
||||
("init", Some(m)) => commands::init::execute(m),
|
||||
("run", Some(m)) => commands::run::execute(m),
|
||||
("upgrade", Some(m)) => commands::upgrade::execute(m),
|
||||
_ => println!("{}", usage()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ mixnode-common = { path = "../common/mixnode-common" }
|
||||
nymsphinx = {path = "../common/nymsphinx" }
|
||||
pemstore = {path = "../common/pemstore"}
|
||||
topology = {path = "../common/topology"}
|
||||
version-checker = { path = "../common/version-checker" }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.1.0"
|
||||
@@ -16,8 +16,9 @@ use crate::config::Config;
|
||||
use clap::ArgMatches;
|
||||
use nymsphinx::params::DEFAULT_NUM_MIX_HOPS;
|
||||
|
||||
pub mod init;
|
||||
pub mod run;
|
||||
pub(crate) mod init;
|
||||
pub(crate) mod run;
|
||||
pub(crate) mod upgrade;
|
||||
|
||||
pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config {
|
||||
let max_layer = DEFAULT_NUM_MIX_HOPS;
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
// Copyright 2020 Nym Technologies SA
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::config::{Config, MISSING_VALUE};
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use config::NymConfig;
|
||||
use std::fmt::Display;
|
||||
use std::process;
|
||||
use version_checker::{parse_version, Version};
|
||||
|
||||
fn print_start_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||
println!(
|
||||
"\n==================\nTrying to upgrade mixnode from {} to {} ...",
|
||||
from, to
|
||||
);
|
||||
}
|
||||
|
||||
fn print_failed_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||
eprintln!(
|
||||
"Upgrade from {} to {} failed!\n==================\n",
|
||||
from, to
|
||||
);
|
||||
}
|
||||
|
||||
fn print_successful_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
|
||||
println!(
|
||||
"Upgrade from {} to {} was successful!\n==================\n",
|
||||
from, to
|
||||
);
|
||||
}
|
||||
|
||||
fn pre_090_upgrade(from: &str, config: Config) -> Config {
|
||||
// this is not extracted to separate function as you only have to manually pass version
|
||||
// if upgrading from pre090 version
|
||||
let from = match from.strip_prefix("v") {
|
||||
Some(stripped) => stripped,
|
||||
None => from,
|
||||
};
|
||||
|
||||
let from = match from.strip_prefix("V") {
|
||||
Some(stripped) => stripped,
|
||||
None => from,
|
||||
};
|
||||
|
||||
let from_version = parse_version(from).expect("invalid version provided!");
|
||||
if from_version.major == 0 && from_version.minor < 8 {
|
||||
// technically this could be implemented, but is there any point in that?
|
||||
eprintln!("upgrading node from before v0.8.0 is not supported. Please run `init` with new binary instead");
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
if (from_version.major == 0 && from_version.minor >= 9) || from_version.major >= 1 {
|
||||
eprintln!("self reported version is higher than 0.9.0. Those releases should have already contained version numbers in config! Make sure you provided correct version");
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
// note: current is guaranteed to not have any `build` information suffix (nor pre-release
|
||||
// information), as this was asserted at the beginning of this command)
|
||||
//
|
||||
// upgrade to current (if it's a 0.9.X) or try to upgrade to 0.9.0 as an intermediate
|
||||
// step in future upgrades (so, for example, we might go 0.8.0 -> 0.9.0 -> 0.10.0)
|
||||
// this way we don't need to have all the crazy paths on how to upgrade from any version to any
|
||||
// other version. We just upgrade one minor version at a time.
|
||||
let current = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
|
||||
let to_version = if current.major == 0 && current.minor == 9 {
|
||||
current
|
||||
} else {
|
||||
Version::new(0, 9, 0)
|
||||
};
|
||||
|
||||
print_start_upgrade(&from_version, &to_version);
|
||||
|
||||
let upgraded_config = config.with_custom_version(to_version.to_string().as_ref());
|
||||
// TODO: THIS IS INCOMPLETE AS ONCE PRESENCE IS REMOVED IN 0.9.0 IT WILL ALSO NEED
|
||||
// TO BE PURGED FROM CONFIG
|
||||
|
||||
upgraded_config.save_to_file(None).unwrap_or_else(|err| {
|
||||
eprintln!("failed to overwrite config file! - {:?}", err);
|
||||
print_failed_upgrade(&from_version, &to_version);
|
||||
process::exit(1);
|
||||
});
|
||||
|
||||
print_successful_upgrade(from_version, to_version);
|
||||
|
||||
upgraded_config
|
||||
}
|
||||
|
||||
pub fn command_args<'a, 'b>() -> App<'a, 'b> {
|
||||
App::new("upgrade").about("Try to upgrade the mixnode")
|
||||
.arg(
|
||||
Arg::with_name("id")
|
||||
.long("id")
|
||||
.help("Id of the nym-mixnode we want to upgrade")
|
||||
.takes_value(true)
|
||||
.required(true),
|
||||
)
|
||||
// the rest of arguments depend on the upgrade path
|
||||
.arg(Arg::with_name("current version")
|
||||
.long("current-version")
|
||||
.help("REQUIRED FOR PRE-0.9.0 UPGRADES. Self provided version of the nym-mixnode if none is available in the config. NOTE: if provided incorrectly, results may be catastrophic.")
|
||||
.takes_value(true)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn execute(matches: &ArgMatches) {
|
||||
let current = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
|
||||
|
||||
// technically this is not a correct way of checking it as a released version might contain valid build identifiers
|
||||
// however, we are not using them ourselves at the moment and hence it should be fine.
|
||||
// if we change our mind, we could easily tweak this code
|
||||
if current.is_prerelease() || !current.build.is_empty() {
|
||||
eprintln!(
|
||||
"Trying to upgrade to a non-released version {}. This is not supported!",
|
||||
current
|
||||
);
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
let id = matches.value_of("id").unwrap();
|
||||
|
||||
let mut existing_config = Config::load_from_file(None, Some(id)).unwrap_or_else(|err| {
|
||||
eprintln!("failed to load existing config file! - {:?}", err);
|
||||
process::exit(1)
|
||||
});
|
||||
|
||||
// versions fields were added in 0.9.0
|
||||
if existing_config.get_version() == MISSING_VALUE {
|
||||
let self_reported_version = matches.value_of("current version").unwrap_or_else(|| {
|
||||
eprintln!(
|
||||
"trying to upgrade from pre v0.9.0 without providing current system version!"
|
||||
);
|
||||
process::exit(1)
|
||||
});
|
||||
|
||||
// upgrades up to 0.9.0
|
||||
existing_config = pre_090_upgrade(self_reported_version, existing_config);
|
||||
}
|
||||
|
||||
let config_version = Version::parse(existing_config.get_version()).unwrap_or_else(|err| {
|
||||
eprintln!("failed to parse node version! - {:?}", err);
|
||||
process::exit(1)
|
||||
});
|
||||
|
||||
if config_version.is_prerelease() || !config_version.build.is_empty() {
|
||||
eprintln!(
|
||||
"Trying to upgrade to from non-released version {}. This is not supported!",
|
||||
current
|
||||
);
|
||||
process::exit(1)
|
||||
}
|
||||
|
||||
// here be upgrade path to 0.10.0 and beyond based on version number from config
|
||||
if config_version == current {
|
||||
println!("You're using the most recent version!");
|
||||
} else {
|
||||
eprintln!("Cannot perform upgrade from {} to {}. Please let the developers know about this issue!", config_version, current);
|
||||
process::exit(1)
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,8 @@ use std::time;
|
||||
pub mod persistence;
|
||||
mod template;
|
||||
|
||||
pub(crate) const MISSING_VALUE: &str = "MISSING VALUE";
|
||||
|
||||
// 'MIXNODE'
|
||||
const DEFAULT_LISTENING_PORT: u16 = 1789;
|
||||
const DEFAULT_DIRECTORY_SERVER: &str = "https://directory.nymtech.net";
|
||||
@@ -84,6 +86,10 @@ impl NymConfig for Config {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn missing_string_value() -> String {
|
||||
MISSING_VALUE.to_string()
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new<S: Into<String>>(id: S) -> Self {
|
||||
Config::default().with_id(id)
|
||||
@@ -198,6 +204,11 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_custom_version(mut self, version: &str) -> Self {
|
||||
self.mixnode.version = version.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
// getters
|
||||
pub fn get_config_file_save_location(&self) -> PathBuf {
|
||||
self.config_directory().join(Self::config_file_name())
|
||||
@@ -262,12 +273,17 @@ impl Config {
|
||||
pub fn get_cache_entry_ttl(&self) -> time::Duration {
|
||||
time::Duration::from_millis(self.debug.cache_entry_ttl)
|
||||
}
|
||||
|
||||
pub fn get_version(&self) -> &str {
|
||||
&self.mixnode.version
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct MixNode {
|
||||
/// Version of the mixnode for which this configuration was created.
|
||||
#[serde(default = "missing_string_value")]
|
||||
version: String,
|
||||
|
||||
/// ID specifies the human readable ID of this particular mixnode.
|
||||
|
||||
@@ -29,6 +29,7 @@ fn main() {
|
||||
.about("Implementation of the Loopix-based Mixnode")
|
||||
.subcommand(commands::init::command_args())
|
||||
.subcommand(commands::run::command_args())
|
||||
.subcommand(commands::upgrade::command_args())
|
||||
.get_matches();
|
||||
|
||||
execute(arg_matches);
|
||||
@@ -38,6 +39,7 @@ fn execute(matches: ArgMatches) {
|
||||
match matches.subcommand() {
|
||||
("init", Some(m)) => commands::init::execute(m),
|
||||
("run", Some(m)) => commands::run::execute(m),
|
||||
("upgrade", Some(m)) => commands::upgrade::execute(m),
|
||||
_ => println!("{}", usage()),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user