Refactor into a service.

This commit is contained in:
Gav Wood 2016-12-11 20:14:18 +01:00
parent 15bb3f9b0a
commit 3aab5dec9b
No known key found for this signature in database
GPG Key ID: C49C1ACA1CC9B252
4 changed files with 132 additions and 96 deletions

2
.gitignore vendored
View File

@ -32,3 +32,5 @@
out/
.vscode
parity.*

View File

@ -24,5 +24,7 @@ extern crate ethabi;
mod updater;
mod operations;
mod service;
pub use updater::{Updater, UpdateFilter, UpdatePolicy, ReleaseInfo, OperationsInfo, CapState};
pub use service::{Service, ReleaseInfo, OperationsInfo, CapState};
pub use updater::{Updater, UpdateFilter, UpdatePolicy};

84
updater/src/service.rs Normal file
View File

@ -0,0 +1,84 @@
// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use util::{H256};
use util::misc::{VersionInfo};
/// Information regarding a particular release of Parity
#[derive(Debug, Clone, PartialEq)]
pub struct ReleaseInfo {
/// Information on the version.
pub version: VersionInfo,
/// Does this release contain critical security updates?
pub is_critical: bool,
/// The latest fork that this release can handle.
pub fork: u64,
/// Our platform's binary, if known.
pub binary: Option<H256>,
}
/// Information on our operations environment.
#[derive(Debug, Clone, PartialEq)]
pub struct OperationsInfo {
/// Our blockchain's latest fork.
pub fork: u64,
/// Last fork our client supports, if known.
pub this_fork: Option<u64>,
/// Information on our track's latest release.
pub track: ReleaseInfo,
/// Information on our minor version's latest release.
pub minor: Option<ReleaseInfo>,
}
/// Information on the current version's consensus capabililty.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CapState {
/// Unknown.
Unknown,
/// Capable of consensus indefinitely.
Capable,
/// Capable of consensus up until a definite block.
CapableUntil(u64),
/// Incapable of consensus since a particular block.
IncapableSince(u64),
}
impl Default for CapState {
fn default() -> Self { CapState::Unknown }
}
pub trait Service: Send + Sync {
/// Is the currently running client capable of supporting the current chain?
/// We default to true if there's no clear information.
fn capability(&self) -> CapState;
/// The release which is ready to be upgraded to, if any. If this returns `Some`, then
/// `execute_upgrade` may be called.
fn upgrade_ready(&self) -> Option<ReleaseInfo>;
/// Actually upgrades the client. Assumes that the binary has been downloaded.
/// @returns `true` on success.
fn execute_upgrade(&self) -> bool;
/// Our version info.
fn version_info(&self) -> VersionInfo;
/// Information gathered concerning the release.
fn info(&self) -> Option<OperationsInfo>;
}

View File

@ -23,6 +23,7 @@ use util::{Address, H160, H256, FixedHash, Mutex, Bytes};
use ethcore::client::{BlockId, BlockChainClient, ChainNotify};
use hash_fetch::{self as fetch, HashFetch};
use operations::Operations;
use service::{Service, ReleaseInfo, OperationsInfo, CapState};
/// Filter for releases.
#[derive(Debug, Eq, PartialEq, Clone)]
@ -56,51 +57,6 @@ impl Default for UpdatePolicy {
}
}
/// Information regarding a particular release of Parity
#[derive(Debug, Clone, PartialEq)]
pub struct ReleaseInfo {
/// Information on the version.
pub version: VersionInfo,
/// Does this release contain critical security updates?
pub is_critical: bool,
/// The latest fork that this release can handle.
pub fork: u64,
/// Our platform's binary, if known.
pub binary: Option<H256>,
}
/// Information on our operations environment.
#[derive(Debug, Clone, PartialEq)]
pub struct OperationsInfo {
/// Our blockchain's latest fork.
pub fork: u64,
/// Last fork our client supports, if known.
pub this_fork: Option<u64>,
/// Information on our track's latest release.
pub track: ReleaseInfo,
/// Information on our minor version's latest release.
pub minor: Option<ReleaseInfo>,
}
/// Information on the current version's consensus capabililty.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CapState {
/// Unknown.
Unknown,
/// Capable of consensus indefinitely.
Capable,
/// Capable of consensus up until a definite block.
CapableUntil(u64),
/// Incapable of consensus since a particular block.
IncapableSince(u64),
}
impl Default for CapState {
fn default() -> Self { CapState::Unknown }
}
#[derive(Debug, Default)]
struct UpdaterState {
latest: Option<OperationsInfo>,
@ -157,60 +113,10 @@ impl Updater {
let r = Arc::new(u);
*r.fetcher.lock() = Some(fetch::Client::new(r.clone()));
*r.weak_self.lock() = Arc::downgrade(&r);
r.poll();
r
}
/// Is the currently running client capable of supporting the current chain?
/// We default to true if there's no clear information.
pub fn capability(&self) -> CapState {
self.state.lock().capability
}
/// The release which is ready to be upgraded to, if any. If this returns `Some`, then
/// `execute_upgrade` may be called.
pub fn upgrade_ready(&self) -> Option<ReleaseInfo> {
self.state.lock().ready.clone()
}
/// Actually upgrades the client. Assumes that the binary has been downloaded.
/// @returns `true` on success.
pub fn execute_upgrade(&self) -> bool {
(|| -> Result<bool, String> {
let mut s = self.state.lock();
if let Some(r) = s.ready.take() {
let p = Self::update_file_name(&r.version);
let n = Self::updates_path("latest");
// TODO: creating then writing is a bit fragile. would be nice to make it atomic.
match fs::File::create(&n).and_then(|mut f| f.write_all(p.as_bytes())) {
Ok(_) => {
info!("Completed upgrade to {}", &r.version);
s.installed = Some(r);
if let Some(ref h) = *self.exit_handler.lock() {
(*h)();
}
Ok(true)
}
Err(e) => {
s.ready = Some(r);
Err(format!("Unable to create soft-link for update {:?}", e))
}
}
} else {
warn!("Execute upgrade called when no upgrade ready.");
Ok(false)
}
})().unwrap_or_else(|e| { warn!("{}", e); false })
}
/// Our version info.
pub fn version_info(&self) -> &VersionInfo { &self.this }
/// Information gathered concerning the release.
pub fn info(&self) -> Option<OperationsInfo> { self.state.lock().latest.clone() }
/// Set a closure to call when we want to restart the client
pub fn set_exit_handler<F>(&self, f: F) where F: Fn() + 'static + Send {
*self.exit_handler.lock() = Some(Box::new(f));
@ -391,3 +297,45 @@ impl fetch::urlhint::ContractClient for Updater {
.call_contract(address, data)
}
}
impl Service for Updater {
fn capability(&self) -> CapState {
self.state.lock().capability
}
fn upgrade_ready(&self) -> Option<ReleaseInfo> {
self.state.lock().ready.clone()
}
fn execute_upgrade(&self) -> bool {
(|| -> Result<bool, String> {
let mut s = self.state.lock();
if let Some(r) = s.ready.take() {
let p = Self::update_file_name(&r.version);
let n = Self::updates_path("latest");
// TODO: creating then writing is a bit fragile. would be nice to make it atomic.
match fs::File::create(&n).and_then(|mut f| f.write_all(p.as_bytes())) {
Ok(_) => {
info!("Completed upgrade to {}", &r.version);
s.installed = Some(r);
if let Some(ref h) = *self.exit_handler.lock() {
(*h)();
}
Ok(true)
}
Err(e) => {
s.ready = Some(r);
Err(format!("Unable to create soft-link for update {:?}", e))
}
}
} else {
warn!("Execute upgrade called when no upgrade ready.");
Ok(false)
}
})().unwrap_or_else(|e| { warn!("{}", e); false })
}
fn version_info(&self) -> VersionInfo { self.this.clone() }
fn info(&self) -> Option<OperationsInfo> { self.state.lock().latest.clone() }
}