Auto-updater improvements (#8078)
* updater: refactor updater flow into state machine * updater: delay update randomly within max range * updater: configurable update delay * updater: split polling and updater state machine step * updater: drop state to avoid deadlocking * updater: fix fetch backoff * updater: fix overflow in update delay calculation * updater: configurable update check frequency * updater: fix update policy frequency comparison * updater: use lazy_static for platform and platform_id_hash * updater: refactor operations contract calls into OperationsClient * updater: make updater generic over operations and fetch client * updater: fix compilation * updater: add testing infrastructure and minimal test * updater: fix minor grumbles * updater: add test for successful updater flow * updater: add test for update delay * updater: add test for update check frequency * updater: mock time and rng for deterministic tests * updater: test backoff on failure * updater: add test for backoff short-circuit on new release * updater: refactor to increase readability * updater: cap maximum backoff to one month * updater: add test for detecting already downloaded update * updater: add test for updater disable on fatal errors * updater: add test for pending outdated fetch * updater: test auto install of updates * updater: add test for capability updates * updater: fix capability update * updater: use ethabi to create event topic filter * updater: decrease maximum backoff to 1 day * updater: cap maximum update delay with upcoming fork block number * updater: receive state mutex guard in updater_step * updater: overload execute_upgrade to take state mutex guard * updater: remove unnecessary clone of latest operations info * updater: remove latest operations info clone when triggering fetch
This commit is contained in:
parent
5e7d42e4a4
commit
dcaff6f4c8
5
Cargo.lock
generated
5
Cargo.lock
generated
@ -2229,13 +2229,18 @@ dependencies = [
|
||||
"ethcore-bytes 0.1.0",
|
||||
"ethereum-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethsync 1.11.0",
|
||||
"keccak-hash 0.1.0",
|
||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-hash-fetch 1.11.0",
|
||||
"parity-version 1.11.0",
|
||||
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"path 0.1.0",
|
||||
"rand 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
//! Test client.
|
||||
|
||||
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrder};
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrder};
|
||||
use std::sync::Arc;
|
||||
use std::collections::{HashMap, BTreeMap};
|
||||
use std::mem;
|
||||
@ -114,6 +114,8 @@ pub struct TestBlockChainClient {
|
||||
pub traces: RwLock<Option<Vec<LocalizedTrace>>>,
|
||||
/// Pruning history size to report.
|
||||
pub history: RwLock<Option<u64>>,
|
||||
/// Is disabled
|
||||
pub disabled: AtomicBool,
|
||||
}
|
||||
|
||||
/// Used for generating test client blocks.
|
||||
@ -180,6 +182,7 @@ impl TestBlockChainClient {
|
||||
first_block: RwLock::new(None),
|
||||
traces: RwLock::new(None),
|
||||
history: RwLock::new(None),
|
||||
disabled: AtomicBool::new(false),
|
||||
};
|
||||
|
||||
// insert genesis hash.
|
||||
@ -356,6 +359,11 @@ impl TestBlockChainClient {
|
||||
pub fn set_history(&self, h: Option<u64>) {
|
||||
*self.history.write() = h;
|
||||
}
|
||||
|
||||
/// Returns true if the client has been disabled.
|
||||
pub fn is_disabled(&self) -> bool {
|
||||
self.disabled.load(AtomicOrder::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_temp_state_db() -> StateDB {
|
||||
@ -679,8 +687,14 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn block_number(&self, _id: BlockId) -> Option<BlockNumber> {
|
||||
unimplemented!()
|
||||
fn block_number(&self, id: BlockId) -> Option<BlockNumber> {
|
||||
match id {
|
||||
BlockId::Number(number) => Some(number),
|
||||
BlockId::Earliest => Some(0),
|
||||
BlockId::Latest => Some(self.chain_info().best_block_number),
|
||||
BlockId::Hash(ref h) =>
|
||||
self.numbers.read().iter().find(|&(_, hash)| hash == h).map(|e| *e.0 as u64)
|
||||
}
|
||||
}
|
||||
|
||||
fn block_body(&self, id: BlockId) -> Option<encoded::Body> {
|
||||
@ -827,7 +841,7 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
|
||||
fn set_spec_name(&self, _: String) { unimplemented!(); }
|
||||
|
||||
fn disable(&self) { unimplemented!(); }
|
||||
fn disable(&self) { self.disabled.store(true, AtomicOrder::Relaxed); }
|
||||
|
||||
fn pruning_info(&self) -> PruningInfo {
|
||||
let best_num = self.chain_info().best_block_number;
|
||||
|
@ -280,6 +280,14 @@ usage! {
|
||||
"--auto-update=[SET]",
|
||||
"Set a releases set to automatically update and install. SET can be one of: all - All updates in the our release track; critical - Only consensus/security updates; none - No updates will be auto-installed.",
|
||||
|
||||
ARG arg_auto_update_delay: (u16) = 100u16, or |c: &Config| c.parity.as_ref()?.auto_update_delay.clone(),
|
||||
"--auto-update-delay=[NUM]",
|
||||
"Specify the maximum number of blocks used for randomly delaying updates.",
|
||||
|
||||
ARG arg_auto_update_check_frequency: (u16) = 20u16, or |c: &Config| c.parity.as_ref()?.auto_update_check_frequency.clone(),
|
||||
"--auto-update-check-frequency=[NUM]",
|
||||
"Specify the number of blocks between each auto-update check.",
|
||||
|
||||
ARG arg_release_track: (String) = "current", or |c: &Config| c.parity.as_ref()?.release_track.clone(),
|
||||
"--release-track=[TRACK]",
|
||||
"Set which release track we should use for updates. TRACK can be one of: stable - Stable releases; beta - Beta releases; nightly - Nightly releases (unstable); testing - Testing releases (do not use); current - Whatever track this executable was released on.",
|
||||
@ -1012,6 +1020,8 @@ struct Operating {
|
||||
mode_timeout: Option<u64>,
|
||||
mode_alarm: Option<u64>,
|
||||
auto_update: Option<String>,
|
||||
auto_update_delay: Option<u16>,
|
||||
auto_update_check_frequency: Option<u16>,
|
||||
release_track: Option<String>,
|
||||
public_node: Option<bool>,
|
||||
no_download: Option<bool>,
|
||||
@ -1454,6 +1464,8 @@ mod tests {
|
||||
arg_mode_timeout: 300u64,
|
||||
arg_mode_alarm: 3600u64,
|
||||
arg_auto_update: "none".into(),
|
||||
arg_auto_update_delay: 200u16,
|
||||
arg_auto_update_check_frequency: 50u16,
|
||||
arg_release_track: "current".into(),
|
||||
flag_public_node: false,
|
||||
flag_no_download: false,
|
||||
@ -1711,6 +1723,8 @@ mod tests {
|
||||
mode_timeout: Some(15u64),
|
||||
mode_alarm: Some(10u64),
|
||||
auto_update: None,
|
||||
auto_update_delay: None,
|
||||
auto_update_check_frequency: None,
|
||||
release_track: None,
|
||||
public_node: None,
|
||||
no_download: None,
|
||||
|
@ -3,6 +3,8 @@ mode = "last"
|
||||
mode_timeout = 300
|
||||
mode_alarm = 3600
|
||||
auto_update = "none"
|
||||
auto_update_delay = 200
|
||||
auto_update_check_frequency = 50
|
||||
release_track = "current"
|
||||
public_node = false
|
||||
no_download = false
|
||||
|
@ -961,6 +961,8 @@ impl Configuration {
|
||||
},
|
||||
path: default_hypervisor_path(),
|
||||
max_size: 128 * 1024 * 1024,
|
||||
max_delay: self.args.arg_auto_update_delay as u64,
|
||||
frequency: self.args.arg_auto_update_check_frequency as u64,
|
||||
})
|
||||
}
|
||||
|
||||
@ -1426,6 +1428,8 @@ mod tests {
|
||||
track: ReleaseTrack::Unknown,
|
||||
path: default_hypervisor_path(),
|
||||
max_size: 128 * 1024 * 1024,
|
||||
max_delay: 100,
|
||||
frequency: 20,
|
||||
},
|
||||
mode: Default::default(),
|
||||
tracing: Default::default(),
|
||||
@ -1491,8 +1495,8 @@ mod tests {
|
||||
fn should_parse_updater_options() {
|
||||
// when
|
||||
let conf0 = parse(&["parity", "--release-track=testing"]);
|
||||
let conf1 = parse(&["parity", "--auto-update", "all", "--no-consensus"]);
|
||||
let conf2 = parse(&["parity", "--no-download", "--auto-update=all", "--release-track=beta"]);
|
||||
let conf1 = parse(&["parity", "--auto-update", "all", "--no-consensus", "--auto-update-delay", "300"]);
|
||||
let conf2 = parse(&["parity", "--no-download", "--auto-update=all", "--release-track=beta", "--auto-update-delay=300", "--auto-update-check-frequency=100"]);
|
||||
let conf3 = parse(&["parity", "--auto-update=xxx"]);
|
||||
|
||||
// then
|
||||
@ -1503,6 +1507,8 @@ mod tests {
|
||||
track: ReleaseTrack::Testing,
|
||||
path: default_hypervisor_path(),
|
||||
max_size: 128 * 1024 * 1024,
|
||||
max_delay: 100,
|
||||
frequency: 20,
|
||||
});
|
||||
assert_eq!(conf1.update_policy().unwrap(), UpdatePolicy {
|
||||
enable_downloading: true,
|
||||
@ -1511,6 +1517,8 @@ mod tests {
|
||||
track: ReleaseTrack::Unknown,
|
||||
path: default_hypervisor_path(),
|
||||
max_size: 128 * 1024 * 1024,
|
||||
max_delay: 300,
|
||||
frequency: 20,
|
||||
});
|
||||
assert_eq!(conf2.update_policy().unwrap(), UpdatePolicy {
|
||||
enable_downloading: false,
|
||||
@ -1519,6 +1527,8 @@ mod tests {
|
||||
track: ReleaseTrack::Beta,
|
||||
path: default_hypervisor_path(),
|
||||
max_size: 128 * 1024 * 1024,
|
||||
max_delay: 300,
|
||||
frequency: 100,
|
||||
});
|
||||
assert!(conf3.update_policy().is_err());
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
keccak-hash = { path = "../util/hash" }
|
||||
lazy_static = "1.0"
|
||||
log = "0.3"
|
||||
ethabi = "5.1"
|
||||
ethabi-derive = "5.0"
|
||||
@ -20,3 +22,8 @@ parking_lot = "0.5"
|
||||
parity-hash-fetch = { path = "../hash-fetch" }
|
||||
parity-version = { path = "../util/version" }
|
||||
path = { path = "../util/path" }
|
||||
rand = "0.4"
|
||||
|
||||
[dev-dependencies]
|
||||
tempdir = "0.3"
|
||||
matches = "0.1"
|
||||
|
@ -556,5 +556,42 @@
|
||||
],
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"name": "ReleaseAdded",
|
||||
"type": "event",
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "client",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "forkBlock",
|
||||
"type": "uint32"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "release",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "track",
|
||||
"type": "uint8"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"name": "semver",
|
||||
"type": "uint24"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"name": "critical",
|
||||
"type": "bool"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
@ -21,10 +21,12 @@ extern crate ethcore;
|
||||
extern crate ethcore_bytes as bytes;
|
||||
extern crate ethereum_types;
|
||||
extern crate ethsync;
|
||||
extern crate keccak_hash as hash;
|
||||
extern crate parity_hash_fetch as hash_fetch;
|
||||
extern crate parity_version as version;
|
||||
extern crate parking_lot;
|
||||
extern crate path;
|
||||
extern crate rand;
|
||||
extern crate semver;
|
||||
extern crate target_info;
|
||||
|
||||
@ -33,8 +35,17 @@ extern crate ethabi_contract;
|
||||
#[macro_use]
|
||||
extern crate ethabi_derive;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate tempdir;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate matches;
|
||||
|
||||
mod updater;
|
||||
mod types;
|
||||
mod service;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -232,9 +232,9 @@ pub fn default_local_path() -> String {
|
||||
}
|
||||
|
||||
/// Default hypervisor path
|
||||
pub fn default_hypervisor_path() -> String {
|
||||
pub fn default_hypervisor_path() -> PathBuf {
|
||||
let app_info = AppInfo { name: PRODUCT_HYPERVISOR, author: AUTHOR };
|
||||
get_app_root(AppDataType::UserData, &app_info).map(|p| p.to_string_lossy().into_owned()).unwrap_or_else(|_| "$HOME/.parity-hypervisor".to_owned())
|
||||
get_app_root(AppDataType::UserData, &app_info).unwrap_or_else(|_| "$HOME/.parity-hypervisor".into())
|
||||
}
|
||||
|
||||
/// Get home directory.
|
||||
|
Loading…
Reference in New Issue
Block a user