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",
|
"ethcore-bytes 0.1.0",
|
||||||
"ethereum-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethereum-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethsync 1.11.0",
|
"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)",
|
"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-hash-fetch 1.11.0",
|
||||||
"parity-version 1.11.0",
|
"parity-version 1.11.0",
|
||||||
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"path 0.1.0",
|
"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)",
|
"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)",
|
"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]]
|
[[package]]
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
//! Test client.
|
//! 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::sync::Arc;
|
||||||
use std::collections::{HashMap, BTreeMap};
|
use std::collections::{HashMap, BTreeMap};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
@ -114,6 +114,8 @@ pub struct TestBlockChainClient {
|
|||||||
pub traces: RwLock<Option<Vec<LocalizedTrace>>>,
|
pub traces: RwLock<Option<Vec<LocalizedTrace>>>,
|
||||||
/// Pruning history size to report.
|
/// Pruning history size to report.
|
||||||
pub history: RwLock<Option<u64>>,
|
pub history: RwLock<Option<u64>>,
|
||||||
|
/// Is disabled
|
||||||
|
pub disabled: AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used for generating test client blocks.
|
/// Used for generating test client blocks.
|
||||||
@ -180,6 +182,7 @@ impl TestBlockChainClient {
|
|||||||
first_block: RwLock::new(None),
|
first_block: RwLock::new(None),
|
||||||
traces: RwLock::new(None),
|
traces: RwLock::new(None),
|
||||||
history: RwLock::new(None),
|
history: RwLock::new(None),
|
||||||
|
disabled: AtomicBool::new(false),
|
||||||
};
|
};
|
||||||
|
|
||||||
// insert genesis hash.
|
// insert genesis hash.
|
||||||
@ -356,6 +359,11 @@ impl TestBlockChainClient {
|
|||||||
pub fn set_history(&self, h: Option<u64>) {
|
pub fn set_history(&self, h: Option<u64>) {
|
||||||
*self.history.write() = h;
|
*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 {
|
pub fn get_temp_state_db() -> StateDB {
|
||||||
@ -679,8 +687,14 @@ impl BlockChainClient for TestBlockChainClient {
|
|||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_number(&self, _id: BlockId) -> Option<BlockNumber> {
|
fn block_number(&self, id: BlockId) -> Option<BlockNumber> {
|
||||||
unimplemented!()
|
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> {
|
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 set_spec_name(&self, _: String) { unimplemented!(); }
|
||||||
|
|
||||||
fn disable(&self) { unimplemented!(); }
|
fn disable(&self) { self.disabled.store(true, AtomicOrder::Relaxed); }
|
||||||
|
|
||||||
fn pruning_info(&self) -> PruningInfo {
|
fn pruning_info(&self) -> PruningInfo {
|
||||||
let best_num = self.chain_info().best_block_number;
|
let best_num = self.chain_info().best_block_number;
|
||||||
|
@ -280,6 +280,14 @@ usage! {
|
|||||||
"--auto-update=[SET]",
|
"--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.",
|
"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(),
|
ARG arg_release_track: (String) = "current", or |c: &Config| c.parity.as_ref()?.release_track.clone(),
|
||||||
"--release-track=[TRACK]",
|
"--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.",
|
"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_timeout: Option<u64>,
|
||||||
mode_alarm: Option<u64>,
|
mode_alarm: Option<u64>,
|
||||||
auto_update: Option<String>,
|
auto_update: Option<String>,
|
||||||
|
auto_update_delay: Option<u16>,
|
||||||
|
auto_update_check_frequency: Option<u16>,
|
||||||
release_track: Option<String>,
|
release_track: Option<String>,
|
||||||
public_node: Option<bool>,
|
public_node: Option<bool>,
|
||||||
no_download: Option<bool>,
|
no_download: Option<bool>,
|
||||||
@ -1454,6 +1464,8 @@ mod tests {
|
|||||||
arg_mode_timeout: 300u64,
|
arg_mode_timeout: 300u64,
|
||||||
arg_mode_alarm: 3600u64,
|
arg_mode_alarm: 3600u64,
|
||||||
arg_auto_update: "none".into(),
|
arg_auto_update: "none".into(),
|
||||||
|
arg_auto_update_delay: 200u16,
|
||||||
|
arg_auto_update_check_frequency: 50u16,
|
||||||
arg_release_track: "current".into(),
|
arg_release_track: "current".into(),
|
||||||
flag_public_node: false,
|
flag_public_node: false,
|
||||||
flag_no_download: false,
|
flag_no_download: false,
|
||||||
@ -1711,6 +1723,8 @@ mod tests {
|
|||||||
mode_timeout: Some(15u64),
|
mode_timeout: Some(15u64),
|
||||||
mode_alarm: Some(10u64),
|
mode_alarm: Some(10u64),
|
||||||
auto_update: None,
|
auto_update: None,
|
||||||
|
auto_update_delay: None,
|
||||||
|
auto_update_check_frequency: None,
|
||||||
release_track: None,
|
release_track: None,
|
||||||
public_node: None,
|
public_node: None,
|
||||||
no_download: None,
|
no_download: None,
|
||||||
|
@ -3,6 +3,8 @@ mode = "last"
|
|||||||
mode_timeout = 300
|
mode_timeout = 300
|
||||||
mode_alarm = 3600
|
mode_alarm = 3600
|
||||||
auto_update = "none"
|
auto_update = "none"
|
||||||
|
auto_update_delay = 200
|
||||||
|
auto_update_check_frequency = 50
|
||||||
release_track = "current"
|
release_track = "current"
|
||||||
public_node = false
|
public_node = false
|
||||||
no_download = false
|
no_download = false
|
||||||
|
@ -961,6 +961,8 @@ impl Configuration {
|
|||||||
},
|
},
|
||||||
path: default_hypervisor_path(),
|
path: default_hypervisor_path(),
|
||||||
max_size: 128 * 1024 * 1024,
|
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,
|
track: ReleaseTrack::Unknown,
|
||||||
path: default_hypervisor_path(),
|
path: default_hypervisor_path(),
|
||||||
max_size: 128 * 1024 * 1024,
|
max_size: 128 * 1024 * 1024,
|
||||||
|
max_delay: 100,
|
||||||
|
frequency: 20,
|
||||||
},
|
},
|
||||||
mode: Default::default(),
|
mode: Default::default(),
|
||||||
tracing: Default::default(),
|
tracing: Default::default(),
|
||||||
@ -1491,8 +1495,8 @@ mod tests {
|
|||||||
fn should_parse_updater_options() {
|
fn should_parse_updater_options() {
|
||||||
// when
|
// when
|
||||||
let conf0 = parse(&["parity", "--release-track=testing"]);
|
let conf0 = parse(&["parity", "--release-track=testing"]);
|
||||||
let conf1 = parse(&["parity", "--auto-update", "all", "--no-consensus"]);
|
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"]);
|
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"]);
|
let conf3 = parse(&["parity", "--auto-update=xxx"]);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
@ -1503,6 +1507,8 @@ mod tests {
|
|||||||
track: ReleaseTrack::Testing,
|
track: ReleaseTrack::Testing,
|
||||||
path: default_hypervisor_path(),
|
path: default_hypervisor_path(),
|
||||||
max_size: 128 * 1024 * 1024,
|
max_size: 128 * 1024 * 1024,
|
||||||
|
max_delay: 100,
|
||||||
|
frequency: 20,
|
||||||
});
|
});
|
||||||
assert_eq!(conf1.update_policy().unwrap(), UpdatePolicy {
|
assert_eq!(conf1.update_policy().unwrap(), UpdatePolicy {
|
||||||
enable_downloading: true,
|
enable_downloading: true,
|
||||||
@ -1511,6 +1517,8 @@ mod tests {
|
|||||||
track: ReleaseTrack::Unknown,
|
track: ReleaseTrack::Unknown,
|
||||||
path: default_hypervisor_path(),
|
path: default_hypervisor_path(),
|
||||||
max_size: 128 * 1024 * 1024,
|
max_size: 128 * 1024 * 1024,
|
||||||
|
max_delay: 300,
|
||||||
|
frequency: 20,
|
||||||
});
|
});
|
||||||
assert_eq!(conf2.update_policy().unwrap(), UpdatePolicy {
|
assert_eq!(conf2.update_policy().unwrap(), UpdatePolicy {
|
||||||
enable_downloading: false,
|
enable_downloading: false,
|
||||||
@ -1519,6 +1527,8 @@ mod tests {
|
|||||||
track: ReleaseTrack::Beta,
|
track: ReleaseTrack::Beta,
|
||||||
path: default_hypervisor_path(),
|
path: default_hypervisor_path(),
|
||||||
max_size: 128 * 1024 * 1024,
|
max_size: 128 * 1024 * 1024,
|
||||||
|
max_delay: 300,
|
||||||
|
frequency: 100,
|
||||||
});
|
});
|
||||||
assert!(conf3.update_policy().is_err());
|
assert!(conf3.update_policy().is_err());
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ license = "GPL-3.0"
|
|||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
keccak-hash = { path = "../util/hash" }
|
||||||
|
lazy_static = "1.0"
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
ethabi = "5.1"
|
ethabi = "5.1"
|
||||||
ethabi-derive = "5.0"
|
ethabi-derive = "5.0"
|
||||||
@ -20,3 +22,8 @@ parking_lot = "0.5"
|
|||||||
parity-hash-fetch = { path = "../hash-fetch" }
|
parity-hash-fetch = { path = "../hash-fetch" }
|
||||||
parity-version = { path = "../util/version" }
|
parity-version = { path = "../util/version" }
|
||||||
path = { path = "../util/path" }
|
path = { path = "../util/path" }
|
||||||
|
rand = "0.4"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tempdir = "0.3"
|
||||||
|
matches = "0.1"
|
||||||
|
@ -556,5 +556,42 @@
|
|||||||
],
|
],
|
||||||
"payable": false,
|
"payable": false,
|
||||||
"type": "function"
|
"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 ethcore_bytes as bytes;
|
||||||
extern crate ethereum_types;
|
extern crate ethereum_types;
|
||||||
extern crate ethsync;
|
extern crate ethsync;
|
||||||
|
extern crate keccak_hash as hash;
|
||||||
extern crate parity_hash_fetch as hash_fetch;
|
extern crate parity_hash_fetch as hash_fetch;
|
||||||
extern crate parity_version as version;
|
extern crate parity_version as version;
|
||||||
extern crate parking_lot;
|
extern crate parking_lot;
|
||||||
extern crate path;
|
extern crate path;
|
||||||
|
extern crate rand;
|
||||||
extern crate semver;
|
extern crate semver;
|
||||||
extern crate target_info;
|
extern crate target_info;
|
||||||
|
|
||||||
@ -33,8 +35,17 @@ extern crate ethabi_contract;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate ethabi_derive;
|
extern crate ethabi_derive;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
extern crate tempdir;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate matches;
|
||||||
|
|
||||||
mod updater;
|
mod updater;
|
||||||
mod types;
|
mod types;
|
||||||
mod service;
|
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
|
/// Default hypervisor path
|
||||||
pub fn default_hypervisor_path() -> String {
|
pub fn default_hypervisor_path() -> PathBuf {
|
||||||
let app_info = AppInfo { name: PRODUCT_HYPERVISOR, author: AUTHOR };
|
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.
|
/// Get home directory.
|
||||||
|
Loading…
Reference in New Issue
Block a user