tor_guardmgr/err.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
//! Declare error types for the `tor-guardmgr` crate.
use futures::task::SpawnError;
use std::sync::Arc;
use std::time::Instant;
use tor_basic_utils::iter::FilterCount;
use tor_error::{Bug, ErrorKind, HasKind};
/// A error caused by a failure to pick a guard.
#[derive(Clone, Debug, thiserror::Error)]
#[non_exhaustive]
pub enum PickGuardError {
/// All members of the current sample were down or unusable.
#[error(
"No usable guards. Rejected {} as down, then {} as pending, then \
{} as unsuitable to purpose, then {} with filter.",
running.display_frac_rejected(),
pending.display_frac_rejected(),
suitable.display_frac_rejected(),
filtered.display_frac_rejected(),
)]
AllGuardsDown {
/// The next time at which any guard will be retriable.
retry_at: Option<Instant>,
/// How many guards we rejected because they had failed too recently.
running: FilterCount,
/// How many guards we rejected because we are already probing them.
pending: FilterCount,
/// How many guards we rejected as unsuitable for the intended application.
suitable: FilterCount,
/// How many guards we rejected because of the provided filter.
filtered: FilterCount,
},
/// We have no usable fallback directories.
#[error(
"No usable fallbacks. Rejected {} as not running, then {} as filtered.",
running.display_frac_rejected(),
filtered.display_frac_rejected()
)]
AllFallbacksDown {
/// The next time at which any fallback directory will back available.
retry_at: Option<Instant>,
/// The number of fallbacks that were believed to be running or down, after applying
/// the filter.
running: FilterCount,
/// The number of fallbacks that satisfied our filter, or did not.
filtered: FilterCount,
},
/// Tried to select guards or fallbacks from an empty list.
#[error("Tried to pick from an empty list")]
NoCandidatesAvailable,
/// An internal programming error occurred.
#[error("Internal error")]
Internal(#[from] Bug),
}
impl tor_error::HasKind for PickGuardError {
fn kind(&self) -> tor_error::ErrorKind {
use tor_error::ErrorKind as EK;
use PickGuardError as E;
match self {
E::AllFallbacksDown { .. } | E::AllGuardsDown { .. } => EK::TorAccessFailed,
E::NoCandidatesAvailable => EK::NoPath,
E::Internal(_) => EK::Internal,
}
}
}
impl tor_error::HasRetryTime for PickGuardError {
fn retry_time(&self) -> tor_error::RetryTime {
use tor_error::RetryTime as RT;
use PickGuardError as E;
match self {
// Some errors contain times that we can just use.
E::AllGuardsDown {
retry_at: Some(when),
..
} => RT::At(*when),
E::AllFallbacksDown {
retry_at: Some(when),
..
} => RT::At(*when),
// If we don't know when the guards/fallbacks will be back up,
// though, then we should suggest a random delay.
E::AllGuardsDown { .. } | E::AllFallbacksDown { .. } => RT::AfterWaiting,
// We were asked to choose some kind of guard that doesn't exist in
// our current universe; that's not going to be come viable down the
// line.
E::NoCandidatesAvailable => RT::Never,
// Don't try to recover from internal errors.
E::Internal(_) => RT::Never,
}
}
}
/// An error caused while creating or updating a guard manager.
#[derive(Clone, Debug, thiserror::Error)]
#[non_exhaustive]
pub enum GuardMgrError {
/// An error manipulating persistent state
#[error("Problem accessing persistent guard state")]
State(#[from] tor_persist::Error),
/// Configuration is not valid or available
#[error("Invalid configuration")]
InvalidConfig(#[from] GuardMgrConfigError),
/// An error that occurred while trying to spawn a daemon task.
#[error("Unable to spawn {spawning}")]
Spawn {
/// What we were trying to spawn.
spawning: &'static str,
/// What happened when we tried to spawn it.
#[source]
cause: Arc<SpawnError>,
},
}
impl HasKind for GuardMgrError {
#[rustfmt::skip] // to preserve table in match
fn kind(&self) -> ErrorKind {
use GuardMgrError as G;
match self {
G::State(e) => e.kind(),
G::InvalidConfig(e) => e.kind(),
G::Spawn{ cause, .. } => cause.kind(),
}
}
}
impl GuardMgrError {
/// Construct a new `GuardMgrError` from a `SpawnError`.
pub(crate) fn from_spawn(spawning: &'static str, err: SpawnError) -> GuardMgrError {
GuardMgrError::Spawn {
spawning,
cause: Arc::new(err),
}
}
}
/// An error encountered while configuring or reconfiguring a guard manager
///
/// When this occurs during initial configuration, it will be returned wrapped
/// up in `GuardMgrError`.
///
/// When it occurs during reconfiguration, it is not exposed to caller:
/// instead, it is converted into a `tor_config::ReconfigureError`.
#[derive(Clone, Debug, thiserror::Error)]
#[non_exhaustive]
pub enum GuardMgrConfigError {
/// Specified configuration requires exclusive access to stored state, which we don't have
#[error("Configuration requires exclusive access to shared state, but another instance of Arti has the lock: {0}")]
NoLock(String),
}
impl From<GuardMgrConfigError> for tor_config::ReconfigureError {
fn from(g: GuardMgrConfigError) -> tor_config::ReconfigureError {
use tor_config::ReconfigureError as R;
use GuardMgrConfigError as G;
match g {
e @ G::NoLock(_) => R::UnsupportedSituation(e.to_string()),
}
}
}
impl HasKind for GuardMgrConfigError {
fn kind(&self) -> ErrorKind {
use ErrorKind as EK;
use GuardMgrConfigError as G;
match self {
// `InvalidConfig` and `FeatureDisabled` aren't right, because
// those should be detected at config build time and reported as `ConfigBuildError`.
// `InvalidConfigTransition` isn't right, because restarting arti won't help.
// Possibly at some point we will allow concurrent artis to work this way.
G::NoLock(_) => EK::NotImplemented,
}
}
}