1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-08-06 06:02:40 +03:00

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.
This commit is contained in:
Quentin Gliech
2024-03-20 15:16:35 +01:00
parent d20e579290
commit 1cf283337b
18 changed files with 108 additions and 126 deletions

View File

@@ -15,6 +15,7 @@
use anyhow::Context;
use camino::Utf8PathBuf;
use clap::Parser;
use figment::Figment;
use mas_config::{ConfigurationSection, RootConfig, SyncConfig};
use mas_storage::SystemClock;
use mas_storage_pg::MIGRATOR;
@@ -67,13 +68,13 @@ enum Subcommand {
}
impl Options {
pub async fn run(self, root: &super::Options) -> anyhow::Result<()> {
pub async fn run(self, figment: &Figment) -> anyhow::Result<()> {
use Subcommand as SC;
match self.subcommand {
SC::Dump { output } => {
let _span = info_span!("cli.config.dump").entered();
let config: RootConfig = root.load_config()?;
let config = RootConfig::extract(figment)?;
let config = serde_yaml::to_string(&config)?;
if let Some(output) = output {
@@ -89,8 +90,8 @@ impl Options {
SC::Check => {
let _span = info_span!("cli.config.check").entered();
let _config: RootConfig = root.load_config()?;
info!(path = ?root.config, "Configuration file looks good");
let _config = RootConfig::extract(figment)?;
info!("Configuration file looks good");
}
SC::Generate { output } => {
@@ -98,7 +99,7 @@ impl Options {
// XXX: we should disallow SeedableRng::from_entropy
let rng = rand_chacha::ChaChaRng::from_entropy();
let config = RootConfig::load_and_generate(rng).await?;
let config = RootConfig::generate(rng).await?;
let config = serde_yaml::to_string(&config)?;
if let Some(output) = output {
@@ -112,7 +113,7 @@ impl Options {
}
SC::Sync { prune, dry_run } => {
let config: SyncConfig = root.load_config()?;
let config = SyncConfig::extract(figment)?;
let clock = SystemClock::default();
let encrypter = config.secrets.encrypter();

View File

@@ -14,7 +14,8 @@
use anyhow::Context;
use clap::Parser;
use mas_config::DatabaseConfig;
use figment::Figment;
use mas_config::{ConfigurationSection, DatabaseConfig};
use mas_storage_pg::MIGRATOR;
use tracing::{info_span, Instrument};
@@ -33,9 +34,9 @@ enum Subcommand {
}
impl Options {
pub async fn run(self, root: &super::Options) -> anyhow::Result<()> {
pub async fn run(self, figment: &Figment) -> anyhow::Result<()> {
let _span = info_span!("cli.database.migrate").entered();
let config: DatabaseConfig = root.load_config()?;
let config = DatabaseConfig::extract(figment)?;
let mut conn = database_connection_from_config(&config).await?;
// Run pending migrations

View File

@@ -13,8 +13,9 @@
// limitations under the License.
use clap::Parser;
use figment::Figment;
use hyper::{Response, Uri};
use mas_config::PolicyConfig;
use mas_config::{ConfigurationSection, PolicyConfig};
use mas_handlers::HttpClientFactory;
use mas_http::HttpServiceExt;
use tokio::io::AsyncWriteExt;
@@ -65,7 +66,7 @@ fn print_headers(parts: &hyper::http::response::Parts) {
impl Options {
#[tracing::instrument(skip_all)]
pub async fn run(self, root: &super::Options) -> anyhow::Result<()> {
pub async fn run(self, figment: &Figment) -> anyhow::Result<()> {
use Subcommand as SC;
let http_client_factory = HttpClientFactory::new();
match self.subcommand {
@@ -120,7 +121,7 @@ impl Options {
SC::Policy => {
let _span = info_span!("cli.debug.policy").entered();
let config: PolicyConfig = root.load_config()?;
let config = PolicyConfig::extract(figment)?;
info!("Loading and compiling the policy module");
let policy_factory = policy_factory_from_config(&config).await?;

View File

@@ -19,7 +19,8 @@
use anyhow::Context;
use clap::Parser;
use mas_config::RootConfig;
use figment::Figment;
use mas_config::{ConfigurationSection, RootConfig};
use mas_handlers::HttpClientFactory;
use mas_http::HttpServiceExt;
use tower::{Service, ServiceExt};
@@ -34,11 +35,11 @@ pub(super) struct Options {}
impl Options {
#[allow(clippy::too_many_lines)]
pub async fn run(self, root: &super::Options) -> anyhow::Result<()> {
pub async fn run(self, figment: &Figment) -> anyhow::Result<()> {
let _span = info_span!("cli.doctor").entered();
info!("💡 Running diagnostics, make sure that both MAS and Synapse are running, and that MAS is using the same configuration files as this tool.");
let config: RootConfig = root.load_config()?;
let config = RootConfig::extract(figment)?;
// We'll need an HTTP client
let http_client_factory = HttpClientFactory::new();

View File

@@ -14,7 +14,8 @@
use anyhow::Context;
use clap::Parser;
use mas_config::{DatabaseConfig, PasswordsConfig};
use figment::Figment;
use mas_config::{ConfigurationSection, DatabaseConfig, PasswordsConfig};
use mas_data_model::{Device, TokenType};
use mas_storage::{
compat::{CompatAccessTokenRepository, CompatSessionRepository},
@@ -89,7 +90,7 @@ enum Subcommand {
impl Options {
#[allow(clippy::too_many_lines)]
pub async fn run(self, root: &super::Options) -> anyhow::Result<()> {
pub async fn run(self, figment: &Figment) -> anyhow::Result<()> {
use Subcommand as SC;
let clock = SystemClock::default();
// XXX: we should disallow SeedableRng::from_entropy
@@ -100,8 +101,8 @@ impl Options {
let _span =
info_span!("cli.manage.set_password", user.username = %username).entered();
let database_config: DatabaseConfig = root.load_config()?;
let passwords_config: PasswordsConfig = root.load_config()?;
let database_config = DatabaseConfig::extract(figment)?;
let passwords_config = PasswordsConfig::extract(figment)?;
let mut conn = database_connection_from_config(&database_config).await?;
let password_manager = password_manager_from_config(&passwords_config).await?;
@@ -136,7 +137,7 @@ impl Options {
)
.entered();
let database_config: DatabaseConfig = root.load_config()?;
let database_config = DatabaseConfig::extract(figment)?;
let mut conn = database_connection_from_config(&database_config).await?;
let txn = conn.begin().await?;
let mut repo = PgRepository::from_conn(txn);
@@ -170,7 +171,7 @@ impl Options {
admin,
device_id,
} => {
let database_config: DatabaseConfig = root.load_config()?;
let database_config = DatabaseConfig::extract(figment)?;
let mut conn = database_connection_from_config(&database_config).await?;
let txn = conn.begin().await?;
let mut repo = PgRepository::from_conn(txn);
@@ -215,7 +216,7 @@ impl Options {
SC::ProvisionAllUsers => {
let _span = info_span!("cli.manage.provision_all_users").entered();
let database_config: DatabaseConfig = root.load_config()?;
let database_config = DatabaseConfig::extract(figment)?;
let mut conn = database_connection_from_config(&database_config).await?;
let mut txn = conn.begin().await?;
@@ -241,7 +242,7 @@ impl Options {
SC::KillSessions { username, dry_run } => {
let _span =
info_span!("cli.manage.kill_sessions", user.username = username).entered();
let database_config: DatabaseConfig = root.load_config()?;
let database_config = DatabaseConfig::extract(figment)?;
let mut conn = database_connection_from_config(&database_config).await?;
let txn = conn.begin().await?;
let mut repo = PgRepository::from_conn(txn);
@@ -361,7 +362,7 @@ impl Options {
deactivate,
} => {
let _span = info_span!("cli.manage.lock_user", user.username = username).entered();
let config: DatabaseConfig = root.load_config()?;
let config = DatabaseConfig::extract(figment)?;
let mut conn = database_connection_from_config(&config).await?;
let txn = conn.begin().await?;
let mut repo = PgRepository::from_conn(txn);
@@ -393,7 +394,7 @@ impl Options {
SC::UnlockUser { username } => {
let _span = info_span!("cli.manage.lock_user", user.username = username).entered();
let config: DatabaseConfig = root.load_config()?;
let config = DatabaseConfig::extract(figment)?;
let mut conn = database_connection_from_config(&config).await?;
let txn = conn.begin().await?;
let mut repo = PgRepository::from_conn(txn);

View File

@@ -12,10 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use anyhow::Context;
use camino::Utf8PathBuf;
use clap::Parser;
use mas_config::ConfigurationSection;
use figment::{
providers::{Env, Format, Yaml},
Figment,
};
mod config;
mod database;
@@ -65,22 +67,23 @@ pub struct Options {
}
impl Options {
pub async fn run(mut self) -> anyhow::Result<()> {
pub async fn run(self, figment: &Figment) -> anyhow::Result<()> {
use Subcommand as S;
match self.subcommand.take() {
Some(S::Config(c)) => c.run(&self).await,
Some(S::Database(c)) => c.run(&self).await,
Some(S::Server(c)) => c.run(&self).await,
Some(S::Worker(c)) => c.run(&self).await,
Some(S::Manage(c)) => c.run(&self).await,
Some(S::Templates(c)) => c.run(&self).await,
Some(S::Debug(c)) => c.run(&self).await,
Some(S::Doctor(c)) => c.run(&self).await,
None => self::server::Options::default().run(&self).await,
match self.subcommand {
Some(S::Config(c)) => c.run(figment).await,
Some(S::Database(c)) => c.run(figment).await,
Some(S::Server(c)) => c.run(figment).await,
Some(S::Worker(c)) => c.run(figment).await,
Some(S::Manage(c)) => c.run(figment).await,
Some(S::Templates(c)) => c.run(figment).await,
Some(S::Debug(c)) => c.run(figment).await,
Some(S::Doctor(c)) => c.run(figment).await,
None => self::server::Options::default().run(figment).await,
}
}
pub fn load_config<T: ConfigurationSection>(&self) -> anyhow::Result<T> {
/// Get a [`Figment`] instance with the configuration loaded
pub fn figment(&self) -> Figment {
let configs = if self.config.is_empty() {
// Read the MAS_CONFIG environment variable
std::env::var("MAS_CONFIG")
@@ -93,7 +96,10 @@ impl Options {
} else {
self.config.clone()
};
let base = Figment::new().merge(Env::prefixed("MAS_").split("_"));
T::load_from_files(&configs).context("could not load configuration")
configs
.into_iter()
.fold(base, |f, path| f.merge(Yaml::file(path)))
}
}

View File

@@ -16,8 +16,9 @@ use std::{collections::BTreeSet, sync::Arc, time::Duration};
use anyhow::Context;
use clap::Parser;
use figment::Figment;
use itertools::Itertools;
use mas_config::AppConfig;
use mas_config::{AppConfig, ClientsConfig, ConfigurationSection, UpstreamOAuth2Config};
use mas_handlers::{ActivityTracker, CookieManager, HttpClientFactory, MetadataCache, SiteConfig};
use mas_listener::{server::Server, shutdown::ShutdownStream};
use mas_matrix_synapse::SynapseConnection;
@@ -63,9 +64,9 @@ pub(super) struct Options {
impl Options {
#[allow(clippy::too_many_lines)]
pub async fn run(self, root: &super::Options) -> anyhow::Result<()> {
pub async fn run(self, figment: &Figment) -> anyhow::Result<()> {
let span = info_span!("cli.run.init").entered();
let config: AppConfig = root.load_config()?;
let config = AppConfig::extract(figment)?;
if self.migrate {
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 {
} else {
// Sync the configuration with the database
let mut conn = pool.acquire().await?;
let clients_config = root.load_config()?;
let upstream_oauth2_config = root.load_config()?;
let clients_config = ClientsConfig::extract(figment)?;
let upstream_oauth2_config = UpstreamOAuth2Config::extract(figment)?;
crate::sync::config_sync(
upstream_oauth2_config,

View File

@@ -13,7 +13,8 @@
// limitations under the License.
use clap::Parser;
use mas_config::{BrandingConfig, MatrixConfig, TemplatesConfig};
use figment::Figment;
use mas_config::{BrandingConfig, ConfigurationSection, MatrixConfig, TemplatesConfig};
use mas_storage::{Clock, SystemClock};
use rand::SeedableRng;
use tracing::info_span;
@@ -33,15 +34,15 @@ enum Subcommand {
}
impl Options {
pub async fn run(self, root: &super::Options) -> anyhow::Result<()> {
pub async fn run(self, figment: &Figment) -> anyhow::Result<()> {
use Subcommand as SC;
match self.subcommand {
SC::Check => {
let _span = info_span!("cli.templates.check").entered();
let template_config: TemplatesConfig = root.load_config()?;
let branding_config: BrandingConfig = root.load_config()?;
let matrix_config: MatrixConfig = root.load_config()?;
let template_config = TemplatesConfig::extract(figment)?;
let branding_config = BrandingConfig::extract(figment)?;
let matrix_config = MatrixConfig::extract(figment)?;
let clock = SystemClock::default();
// XXX: we should disallow SeedableRng::from_entropy

View File

@@ -13,7 +13,8 @@
// limitations under the License.
use clap::Parser;
use mas_config::AppConfig;
use figment::Figment;
use mas_config::{AppConfig, ConfigurationSection};
use mas_handlers::HttpClientFactory;
use mas_matrix_synapse::SynapseConnection;
use mas_router::UrlBuilder;
@@ -29,9 +30,9 @@ use crate::util::{database_pool_from_config, mailer_from_config, templates_from_
pub(super) struct Options {}
impl Options {
pub async fn run(self, root: &super::Options) -> anyhow::Result<()> {
pub async fn run(self, figment: &Figment) -> anyhow::Result<()> {
let span = info_span!("cli.worker.init").entered();
let config: AppConfig = root.load_config()?;
let config = AppConfig::extract(figment)?;
// Connect to the database
info!("Connecting to the database");