Skip to content
This repository was archived by the owner on Sep 10, 2024. It is now read-only.

Commit 1cf2833

Browse files
committed
Load the configuration from a common Figment instance
This should avoid loading the same files multiple times. It should also make it easier to do post-processing on the configuration, like validation. This does deprecate one undocumented feature: the ability to override some fields during the configuration generation using environment variables.
1 parent d20e579 commit 1cf2833

File tree

18 files changed

+108
-126
lines changed

18 files changed

+108
-126
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ features = ["serde", "clock"]
7878
version = "4.5.3"
7979
features = ["derive"]
8080

81+
# Configuration loading
82+
[workspace.dependencies.figment]
83+
version = "0.10.15"
84+
features = ["env", "yaml", "test"]
85+
8186
# HTTP headers
8287
[workspace.dependencies.headers]
8388
version = "0.3.9"

crates/cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ axum = "0.6.20"
1717
camino.workspace = true
1818
clap.workspace = true
1919
dotenvy = "0.15.7"
20+
figment.workspace = true
2021
httpdate = "1.0.3"
2122
hyper.workspace = true
2223
ipnetwork = "0.20.0"

crates/cli/src/commands/config.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use anyhow::Context;
1616
use camino::Utf8PathBuf;
1717
use clap::Parser;
18+
use figment::Figment;
1819
use mas_config::{ConfigurationSection, RootConfig, SyncConfig};
1920
use mas_storage::SystemClock;
2021
use mas_storage_pg::MIGRATOR;
@@ -67,13 +68,13 @@ enum Subcommand {
6768
}
6869

6970
impl Options {
70-
pub async fn run(self, root: &super::Options) -> anyhow::Result<()> {
71+
pub async fn run(self, figment: &Figment) -> anyhow::Result<()> {
7172
use Subcommand as SC;
7273
match self.subcommand {
7374
SC::Dump { output } => {
7475
let _span = info_span!("cli.config.dump").entered();
7576

76-
let config: RootConfig = root.load_config()?;
77+
let config = RootConfig::extract(figment)?;
7778
let config = serde_yaml::to_string(&config)?;
7879

7980
if let Some(output) = output {
@@ -89,16 +90,16 @@ impl Options {
8990
SC::Check => {
9091
let _span = info_span!("cli.config.check").entered();
9192

92-
let _config: RootConfig = root.load_config()?;
93-
info!(path = ?root.config, "Configuration file looks good");
93+
let _config = RootConfig::extract(figment)?;
94+
info!("Configuration file looks good");
9495
}
9596

9697
SC::Generate { output } => {
9798
let _span = info_span!("cli.config.generate").entered();
9899

99100
// XXX: we should disallow SeedableRng::from_entropy
100101
let rng = rand_chacha::ChaChaRng::from_entropy();
101-
let config = RootConfig::load_and_generate(rng).await?;
102+
let config = RootConfig::generate(rng).await?;
102103
let config = serde_yaml::to_string(&config)?;
103104

104105
if let Some(output) = output {
@@ -112,7 +113,7 @@ impl Options {
112113
}
113114

114115
SC::Sync { prune, dry_run } => {
115-
let config: SyncConfig = root.load_config()?;
116+
let config = SyncConfig::extract(figment)?;
116117
let clock = SystemClock::default();
117118
let encrypter = config.secrets.encrypter();
118119

crates/cli/src/commands/database.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414

1515
use anyhow::Context;
1616
use clap::Parser;
17-
use mas_config::DatabaseConfig;
17+
use figment::Figment;
18+
use mas_config::{ConfigurationSection, DatabaseConfig};
1819
use mas_storage_pg::MIGRATOR;
1920
use tracing::{info_span, Instrument};
2021

@@ -33,9 +34,9 @@ enum Subcommand {
3334
}
3435

3536
impl Options {
36-
pub async fn run(self, root: &super::Options) -> anyhow::Result<()> {
37+
pub async fn run(self, figment: &Figment) -> anyhow::Result<()> {
3738
let _span = info_span!("cli.database.migrate").entered();
38-
let config: DatabaseConfig = root.load_config()?;
39+
let config = DatabaseConfig::extract(figment)?;
3940
let mut conn = database_connection_from_config(&config).await?;
4041

4142
// Run pending migrations

crates/cli/src/commands/debug.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313
// limitations under the License.
1414

1515
use clap::Parser;
16+
use figment::Figment;
1617
use hyper::{Response, Uri};
17-
use mas_config::PolicyConfig;
18+
use mas_config::{ConfigurationSection, PolicyConfig};
1819
use mas_handlers::HttpClientFactory;
1920
use mas_http::HttpServiceExt;
2021
use tokio::io::AsyncWriteExt;
@@ -65,7 +66,7 @@ fn print_headers(parts: &hyper::http::response::Parts) {
6566

6667
impl Options {
6768
#[tracing::instrument(skip_all)]
68-
pub async fn run(self, root: &super::Options) -> anyhow::Result<()> {
69+
pub async fn run(self, figment: &Figment) -> anyhow::Result<()> {
6970
use Subcommand as SC;
7071
let http_client_factory = HttpClientFactory::new();
7172
match self.subcommand {
@@ -120,7 +121,7 @@ impl Options {
120121

121122
SC::Policy => {
122123
let _span = info_span!("cli.debug.policy").entered();
123-
let config: PolicyConfig = root.load_config()?;
124+
let config = PolicyConfig::extract(figment)?;
124125
info!("Loading and compiling the policy module");
125126
let policy_factory = policy_factory_from_config(&config).await?;
126127

crates/cli/src/commands/doctor.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
2020
use anyhow::Context;
2121
use clap::Parser;
22-
use mas_config::RootConfig;
22+
use figment::Figment;
23+
use mas_config::{ConfigurationSection, RootConfig};
2324
use mas_handlers::HttpClientFactory;
2425
use mas_http::HttpServiceExt;
2526
use tower::{Service, ServiceExt};
@@ -34,11 +35,11 @@ pub(super) struct Options {}
3435

3536
impl Options {
3637
#[allow(clippy::too_many_lines)]
37-
pub async fn run(self, root: &super::Options) -> anyhow::Result<()> {
38+
pub async fn run(self, figment: &Figment) -> anyhow::Result<()> {
3839
let _span = info_span!("cli.doctor").entered();
3940
info!("💡 Running diagnostics, make sure that both MAS and Synapse are running, and that MAS is using the same configuration files as this tool.");
4041

41-
let config: RootConfig = root.load_config()?;
42+
let config = RootConfig::extract(figment)?;
4243

4344
// We'll need an HTTP client
4445
let http_client_factory = HttpClientFactory::new();

crates/cli/src/commands/manage.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414

1515
use anyhow::Context;
1616
use clap::Parser;
17-
use mas_config::{DatabaseConfig, PasswordsConfig};
17+
use figment::Figment;
18+
use mas_config::{ConfigurationSection, DatabaseConfig, PasswordsConfig};
1819
use mas_data_model::{Device, TokenType};
1920
use mas_storage::{
2021
compat::{CompatAccessTokenRepository, CompatSessionRepository},
@@ -89,7 +90,7 @@ enum Subcommand {
8990

9091
impl Options {
9192
#[allow(clippy::too_many_lines)]
92-
pub async fn run(self, root: &super::Options) -> anyhow::Result<()> {
93+
pub async fn run(self, figment: &Figment) -> anyhow::Result<()> {
9394
use Subcommand as SC;
9495
let clock = SystemClock::default();
9596
// XXX: we should disallow SeedableRng::from_entropy
@@ -100,8 +101,8 @@ impl Options {
100101
let _span =
101102
info_span!("cli.manage.set_password", user.username = %username).entered();
102103

103-
let database_config: DatabaseConfig = root.load_config()?;
104-
let passwords_config: PasswordsConfig = root.load_config()?;
104+
let database_config = DatabaseConfig::extract(figment)?;
105+
let passwords_config = PasswordsConfig::extract(figment)?;
105106

106107
let mut conn = database_connection_from_config(&database_config).await?;
107108
let password_manager = password_manager_from_config(&passwords_config).await?;
@@ -136,7 +137,7 @@ impl Options {
136137
)
137138
.entered();
138139

139-
let database_config: DatabaseConfig = root.load_config()?;
140+
let database_config = DatabaseConfig::extract(figment)?;
140141
let mut conn = database_connection_from_config(&database_config).await?;
141142
let txn = conn.begin().await?;
142143
let mut repo = PgRepository::from_conn(txn);
@@ -170,7 +171,7 @@ impl Options {
170171
admin,
171172
device_id,
172173
} => {
173-
let database_config: DatabaseConfig = root.load_config()?;
174+
let database_config = DatabaseConfig::extract(figment)?;
174175
let mut conn = database_connection_from_config(&database_config).await?;
175176
let txn = conn.begin().await?;
176177
let mut repo = PgRepository::from_conn(txn);
@@ -215,7 +216,7 @@ impl Options {
215216

216217
SC::ProvisionAllUsers => {
217218
let _span = info_span!("cli.manage.provision_all_users").entered();
218-
let database_config: DatabaseConfig = root.load_config()?;
219+
let database_config = DatabaseConfig::extract(figment)?;
219220
let mut conn = database_connection_from_config(&database_config).await?;
220221
let mut txn = conn.begin().await?;
221222

@@ -241,7 +242,7 @@ impl Options {
241242
SC::KillSessions { username, dry_run } => {
242243
let _span =
243244
info_span!("cli.manage.kill_sessions", user.username = username).entered();
244-
let database_config: DatabaseConfig = root.load_config()?;
245+
let database_config = DatabaseConfig::extract(figment)?;
245246
let mut conn = database_connection_from_config(&database_config).await?;
246247
let txn = conn.begin().await?;
247248
let mut repo = PgRepository::from_conn(txn);
@@ -361,7 +362,7 @@ impl Options {
361362
deactivate,
362363
} => {
363364
let _span = info_span!("cli.manage.lock_user", user.username = username).entered();
364-
let config: DatabaseConfig = root.load_config()?;
365+
let config = DatabaseConfig::extract(figment)?;
365366
let mut conn = database_connection_from_config(&config).await?;
366367
let txn = conn.begin().await?;
367368
let mut repo = PgRepository::from_conn(txn);
@@ -393,7 +394,7 @@ impl Options {
393394

394395
SC::UnlockUser { username } => {
395396
let _span = info_span!("cli.manage.lock_user", user.username = username).entered();
396-
let config: DatabaseConfig = root.load_config()?;
397+
let config = DatabaseConfig::extract(figment)?;
397398
let mut conn = database_connection_from_config(&config).await?;
398399
let txn = conn.begin().await?;
399400
let mut repo = PgRepository::from_conn(txn);

crates/cli/src/commands/mod.rs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
use anyhow::Context;
1615
use camino::Utf8PathBuf;
1716
use clap::Parser;
18-
use mas_config::ConfigurationSection;
17+
use figment::{
18+
providers::{Env, Format, Yaml},
19+
Figment,
20+
};
1921

2022
mod config;
2123
mod database;
@@ -65,22 +67,23 @@ pub struct Options {
6567
}
6668

6769
impl Options {
68-
pub async fn run(mut self) -> anyhow::Result<()> {
70+
pub async fn run(self, figment: &Figment) -> anyhow::Result<()> {
6971
use Subcommand as S;
70-
match self.subcommand.take() {
71-
Some(S::Config(c)) => c.run(&self).await,
72-
Some(S::Database(c)) => c.run(&self).await,
73-
Some(S::Server(c)) => c.run(&self).await,
74-
Some(S::Worker(c)) => c.run(&self).await,
75-
Some(S::Manage(c)) => c.run(&self).await,
76-
Some(S::Templates(c)) => c.run(&self).await,
77-
Some(S::Debug(c)) => c.run(&self).await,
78-
Some(S::Doctor(c)) => c.run(&self).await,
79-
None => self::server::Options::default().run(&self).await,
72+
match self.subcommand {
73+
Some(S::Config(c)) => c.run(figment).await,
74+
Some(S::Database(c)) => c.run(figment).await,
75+
Some(S::Server(c)) => c.run(figment).await,
76+
Some(S::Worker(c)) => c.run(figment).await,
77+
Some(S::Manage(c)) => c.run(figment).await,
78+
Some(S::Templates(c)) => c.run(figment).await,
79+
Some(S::Debug(c)) => c.run(figment).await,
80+
Some(S::Doctor(c)) => c.run(figment).await,
81+
None => self::server::Options::default().run(figment).await,
8082
}
8183
}
8284

83-
pub fn load_config<T: ConfigurationSection>(&self) -> anyhow::Result<T> {
85+
/// Get a [`Figment`] instance with the configuration loaded
86+
pub fn figment(&self) -> Figment {
8487
let configs = if self.config.is_empty() {
8588
// Read the MAS_CONFIG environment variable
8689
std::env::var("MAS_CONFIG")
@@ -93,7 +96,10 @@ impl Options {
9396
} else {
9497
self.config.clone()
9598
};
99+
let base = Figment::new().merge(Env::prefixed("MAS_").split("_"));
96100

97-
T::load_from_files(&configs).context("could not load configuration")
101+
configs
102+
.into_iter()
103+
.fold(base, |f, path| f.merge(Yaml::file(path)))
98104
}
99105
}

crates/cli/src/commands/server.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ use std::{collections::BTreeSet, sync::Arc, time::Duration};
1616

1717
use anyhow::Context;
1818
use clap::Parser;
19+
use figment::Figment;
1920
use itertools::Itertools;
20-
use mas_config::AppConfig;
21+
use mas_config::{AppConfig, ClientsConfig, ConfigurationSection, UpstreamOAuth2Config};
2122
use mas_handlers::{ActivityTracker, CookieManager, HttpClientFactory, MetadataCache, SiteConfig};
2223
use mas_listener::{server::Server, shutdown::ShutdownStream};
2324
use mas_matrix_synapse::SynapseConnection;
@@ -63,9 +64,9 @@ pub(super) struct Options {
6364

6465
impl Options {
6566
#[allow(clippy::too_many_lines)]
66-
pub async fn run(self, root: &super::Options) -> anyhow::Result<()> {
67+
pub async fn run(self, figment: &Figment) -> anyhow::Result<()> {
6768
let span = info_span!("cli.run.init").entered();
68-
let config: AppConfig = root.load_config()?;
69+
let config = AppConfig::extract(figment)?;
6970

7071
if self.migrate {
7172
warn!("The `--migrate` flag is deprecated and will be removed in a future release. Please use `--no-migrate` to disable automatic migrations on startup.");
@@ -101,8 +102,8 @@ impl Options {
101102
} else {
102103
// Sync the configuration with the database
103104
let mut conn = pool.acquire().await?;
104-
let clients_config = root.load_config()?;
105-
let upstream_oauth2_config = root.load_config()?;
105+
let clients_config = ClientsConfig::extract(figment)?;
106+
let upstream_oauth2_config = UpstreamOAuth2Config::extract(figment)?;
106107

107108
crate::sync::config_sync(
108109
upstream_oauth2_config,

0 commit comments

Comments
 (0)