Skip to content

Commit ff9fd96

Browse files
authored
Merge pull request #7431 from Turbo87/less-panic-more-qms
Return `anyhow::Result` from more functions instead of panicking
2 parents 9efa097 + 5e02da3 commit ff9fd96

File tree

13 files changed

+61
-52
lines changed

13 files changed

+61
-52
lines changed

crates_io_index/repo.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ pub struct RepositoryConfig {
1313
}
1414

1515
impl RepositoryConfig {
16-
pub fn from_environment() -> Self {
16+
pub fn from_environment() -> anyhow::Result<Self> {
1717
let username = dotenvy::var("GIT_HTTP_USER");
1818
let password = dotenvy::var("GIT_HTTP_PWD").map(SecretString::from);
1919
let http_url = dotenvy::var("GIT_REPO_URL");
@@ -38,30 +38,30 @@ impl RepositoryConfig {
3838
String::from_utf8(key).expect("failed to convert the ssh key to a string");
3939
let credentials = Credentials::Ssh { key: key.into() };
4040

41-
Self {
41+
Ok(Self {
4242
index_location,
4343
credentials,
44-
}
44+
})
4545
}
4646
(Ok(username), Ok(password), Ok(http_url), Err(_), Err(_)) => {
4747
let index_location = Url::parse(&http_url).expect("failed to parse GIT_REPO_URL");
4848
let credentials = Credentials::Http { username, password };
4949

50-
Self {
50+
Ok(Self {
5151
index_location,
5252
credentials,
53-
}
53+
})
5454
}
5555
(_, _, Ok(http_url), _, _) => {
5656
let index_location = Url::parse(&http_url).expect("failed to parse GIT_REPO_URL");
5757
let credentials = Credentials::Missing;
5858

59-
Self {
59+
Ok(Self {
6060
index_location,
6161
credentials,
62-
}
62+
})
6363
}
64-
_ => panic!("must have `GIT_REPO_URL` defined"),
64+
_ => Err(anyhow!("must have `GIT_REPO_URL` defined")),
6565
}
6666
}
6767
}

src/admin/git_import.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub struct Opts {
3030
pub fn run(opts: Opts) -> anyhow::Result<()> {
3131
let mut conn = db::oneoff_connection()?;
3232
println!("fetching git repo");
33-
let config = RepositoryConfig::from_environment();
33+
let config = RepositoryConfig::from_environment()?;
3434
let repo = Repository::open(&config)?;
3535
repo.reset_head()?;
3636
println!("HEAD is at {}", repo.head_oid()?);

src/admin/migrate.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ pub struct Opts;
1616

1717
pub fn run(_opts: Opts) -> Result<(), Error> {
1818
let config = crate::config::DatabasePools::full_from_environment(
19-
&crate::config::Base::from_environment(),
20-
);
19+
&crate::config::Base::from_environment()?,
20+
)?;
2121

2222
// TODO: Refactor logic so that we can also check things from App::new() here.
2323
// If the app will panic due to bad configuration, it is better to error in the release phase

src/admin/upload_index.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub fn run(opts: Opts) -> anyhow::Result<()> {
1818
let storage = Storage::from_environment();
1919

2020
println!("fetching git repo");
21-
let config = RepositoryConfig::from_environment();
21+
let config = RepositoryConfig::from_environment()?;
2222
let repo = Repository::open(&config)?;
2323
repo.reset_head()?;
2424
println!("HEAD is at {}", repo.head_oid()?);

src/bin/background-worker.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use std::sync::Arc;
3232
use std::thread::sleep;
3333
use std::time::{Duration, Instant};
3434

35-
fn main() {
35+
fn main() -> anyhow::Result<()> {
3636
let _sentry = crates_io::sentry::init();
3737

3838
// Initialize logging
@@ -42,7 +42,7 @@ fn main() {
4242

4343
info!("Booting runner");
4444

45-
let config = config::Server::default();
45+
let config = config::Server::from_environment()?;
4646

4747
if config.db.are_all_read_only() {
4848
loop {
@@ -65,7 +65,7 @@ fn main() {
6565
}
6666

6767
let clone_start = Instant::now();
68-
let repository_config = RepositoryConfig::from_environment();
68+
let repository_config = RepositoryConfig::from_environment()?;
6969
let repository = Repository::open(&repository_config).expect("Failed to clone index");
7070

7171
let clone_duration = clone_start.elapsed();

src/bin/server.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ use tower::Layer;
1818

1919
const CORE_THREADS: usize = 4;
2020

21-
fn main() -> Result<(), Box<dyn std::error::Error>> {
21+
fn main() -> anyhow::Result<()> {
2222
let _sentry = crates_io::sentry::init();
2323

2424
// Initialize logging
2525
crates_io::util::tracing::init();
2626

2727
let _span = info_span!("server.run");
2828

29-
let config = crates_io::config::Server::default();
29+
let config = crates_io::config::Server::from_environment()?;
3030
let client = Client::new();
3131
let app = Arc::new(App::new(config, Some(client)));
3232

src/config/balance_capacity.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ pub struct BalanceCapacityConfig {
1010
}
1111

1212
impl BalanceCapacityConfig {
13-
pub fn from_environment() -> Self {
14-
Self {
13+
pub fn from_environment() -> anyhow::Result<Self> {
14+
Ok(Self {
1515
report_only: env::var("WEB_CAPACITY_REPORT_ONLY").is_ok(),
1616
log_total_at_count: env_optional("WEB_CAPACITY_LOG_TOTAL_AT_COUNT").unwrap_or(50),
1717
// The following are a percentage of `db_capacity`
1818
log_at_percentage: env_optional("WEB_CAPACITY_LOG_PCT").unwrap_or(50),
1919
throttle_at_percentage: env_optional("WEB_CAPACITY_THROTTLE_PCT").unwrap_or(70),
2020
dl_only_at_percentage: env_optional("WEB_CAPACITY_DL_ONLY_PCT").unwrap_or(80),
21-
}
21+
})
2222
}
2323
}

src/config/base.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ pub struct Base {
99
}
1010

1111
impl Base {
12-
pub fn from_environment() -> Self {
12+
pub fn from_environment() -> anyhow::Result<Self> {
1313
let env = match dotenvy::var("HEROKU") {
1414
Ok(_) => Env::Production,
1515
_ => Env::Development,
1616
};
1717

18-
Self { env }
18+
Ok(Self { env })
1919
}
2020
}

src/config/database_pools.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ impl DatabasePools {
6363
/// # Panics
6464
///
6565
/// This function panics if `DB_OFFLINE=leader` but `READ_ONLY_REPLICA_URL` is unset.
66-
pub fn full_from_environment(base: &Base) -> Self {
66+
pub fn full_from_environment(base: &Base) -> anyhow::Result<Self> {
6767
let leader_url = env("DATABASE_URL").into();
6868
let follower_url = dotenvy::var("READ_ONLY_REPLICA_URL").map(Into::into).ok();
6969
let read_only_mode = dotenvy::var("READ_ONLY_MODE").is_ok();
@@ -110,7 +110,7 @@ impl DatabasePools {
110110

111111
let enforce_tls = base.env == Env::Production;
112112

113-
match dotenvy::var("DB_OFFLINE").as_deref() {
113+
Ok(match dotenvy::var("DB_OFFLINE").as_deref() {
114114
// The actual leader is down, use the follower in read-only mode as the primary and
115115
// don't configure a replica.
116116
Ok("leader") => Self {
@@ -165,6 +165,6 @@ impl DatabasePools {
165165
helper_threads,
166166
enforce_tls,
167167
},
168-
}
168+
})
169169
}
170170
}

src/config/sentry.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::env_optional;
2+
use anyhow::Context;
23
use sentry::types::Dsn;
34
use sentry::IntoDsn;
45

@@ -10,22 +11,25 @@ pub struct SentryConfig {
1011
}
1112

1213
impl SentryConfig {
13-
pub fn from_environment() -> Self {
14+
pub fn from_environment() -> anyhow::Result<Self> {
1415
let dsn = dotenvy::var("SENTRY_DSN_API")
1516
.ok()
1617
.into_dsn()
17-
.expect("SENTRY_DSN_API is not a valid Sentry DSN value");
18+
.context("SENTRY_DSN_API is not a valid Sentry DSN value")?;
1819

19-
let environment = dsn.as_ref().map(|_| {
20-
dotenvy::var("SENTRY_ENV_API")
21-
.expect("SENTRY_ENV_API must be set when using SENTRY_DSN_API")
22-
});
20+
let environment = match dsn {
21+
None => None,
22+
Some(_) => Some(
23+
dotenvy::var("SENTRY_ENV_API")
24+
.context("SENTRY_ENV_API must be set when using SENTRY_DSN_API")?,
25+
),
26+
};
2327

24-
Self {
28+
Ok(Self {
2529
dsn,
2630
environment,
2731
release: dotenvy::var("HEROKU_SLUG_COMMIT").ok(),
2832
traces_sample_rate: env_optional("SENTRY_TRACES_SAMPLE_RATE").unwrap_or(0.0),
29-
}
33+
})
3034
}
3135
}

src/config/server.rs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ pub struct Server {
6565
pub content_security_policy: Option<HeaderValue>,
6666
}
6767

68-
impl Default for Server {
68+
impl Server {
6969
/// Returns a default value for the application's config
7070
///
7171
/// Sets the following default values:
@@ -102,7 +102,7 @@ impl Default for Server {
102102
/// # Panics
103103
///
104104
/// This function panics if the Server configuration is invalid.
105-
fn default() -> Self {
105+
pub fn from_environment() -> anyhow::Result<Self> {
106106
let docker = dotenvy::var("DEV_DOCKER").is_ok();
107107
let heroku = dotenvy::var("HEROKU").is_ok();
108108

@@ -114,7 +114,7 @@ impl Default for Server {
114114

115115
let port = env_optional("PORT").unwrap_or(8888);
116116

117-
let allowed_origins = AllowedOrigins::from_default_env();
117+
let allowed_origins = AllowedOrigins::from_default_env()?;
118118
let page_offset_ua_blocklist = match env_optional::<String>("WEB_PAGE_OFFSET_UA_BLOCKLIST")
119119
{
120120
None => vec![],
@@ -128,11 +128,10 @@ impl Default for Server {
128128
Some(s) => s
129129
.split(',')
130130
.map(parse_cidr_block)
131-
.collect::<Result<_, _>>()
132-
.unwrap(),
131+
.collect::<Result<_, _>>()?,
133132
};
134133

135-
let base = Base::from_environment();
134+
let base = Base::from_environment()?;
136135
let excluded_crate_names = match env_optional::<String>("EXCLUDED_CRATE_NAMES") {
137136
None => vec![],
138137
Some(s) if s.is_empty() => vec![],
@@ -176,8 +175,8 @@ impl Default for Server {
176175
cdn_domain = storage.cdn_prefix.as_ref().map(|cdn_prefix| format!("https://{cdn_prefix}")).unwrap_or_default()
177176
);
178177

179-
Server {
180-
db: DatabasePools::full_from_environment(&base),
178+
Ok(Server {
179+
db: DatabasePools::full_from_environment(&base)?,
181180
storage,
182181
base,
183182
ip,
@@ -221,11 +220,11 @@ impl Default for Server {
221220
),
222221
cdn_user_agent: dotenvy::var("WEB_CDN_USER_AGENT")
223222
.unwrap_or_else(|_| "Amazon CloudFront".into()),
224-
balance_capacity: BalanceCapacityConfig::from_environment(),
223+
balance_capacity: BalanceCapacityConfig::from_environment()?,
225224
serve_dist: true,
226225
serve_html: true,
227-
content_security_policy: Some(content_security_policy.parse().unwrap()),
228-
}
226+
content_security_policy: Some(content_security_policy.parse()?),
227+
})
229228
}
230229
}
231230

@@ -291,13 +290,13 @@ fn parse_traffic_patterns(patterns: &str) -> impl Iterator<Item = (&str, &str)>
291290
pub struct AllowedOrigins(Vec<String>);
292291

293292
impl AllowedOrigins {
294-
pub fn from_default_env() -> Self {
293+
pub fn from_default_env() -> anyhow::Result<Self> {
295294
let allowed_origins = env("WEB_ALLOWED_ORIGINS")
296295
.split(',')
297296
.map(ToString::to_string)
298297
.collect();
299298

300-
Self(allowed_origins)
299+
Ok(Self(allowed_origins))
301300
}
302301

303302
pub fn contains(&self, value: &HeaderValue) -> bool {

src/db.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,9 @@ pub fn oneoff_connection_with_config(
174174
PgConnection::establish(&url)
175175
}
176176

177-
pub fn oneoff_connection() -> ConnectionResult<PgConnection> {
178-
let config = config::DatabasePools::full_from_environment(&config::Base::from_environment());
179-
oneoff_connection_with_config(&config)
177+
pub fn oneoff_connection() -> anyhow::Result<PgConnection> {
178+
let config = config::DatabasePools::full_from_environment(&config::Base::from_environment()?)?;
179+
oneoff_connection_with_config(&config).map_err(Into::into)
180180
}
181181

182182
pub fn connection_url(config: &config::DatabasePools, url: &str) -> String {

src/sentry/mod.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,14 @@ use std::sync::Arc;
1010
///
1111
/// `HEROKU_SLUG_COMMIT`, if present, will be used as the `release` property
1212
/// on all events.
13-
pub fn init() -> ClientInitGuard {
14-
let config = SentryConfig::from_environment();
13+
pub fn init() -> Option<ClientInitGuard> {
14+
let config = match SentryConfig::from_environment() {
15+
Ok(config) => config,
16+
Err(error) => {
17+
warn!(%error, "Failed to read Sentry configuration from environment");
18+
return None;
19+
}
20+
};
1521

1622
let traces_sampler = move |ctx: &TransactionContext| -> f32 {
1723
if let Some(sampled) = ctx.sampled() {
@@ -55,5 +61,5 @@ pub fn init() -> ClientInitGuard {
5561
..Default::default()
5662
};
5763

58-
sentry::init(opts)
64+
Some(sentry::init(opts))
5965
}

0 commit comments

Comments
 (0)