You've already forked authentication-service
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:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -3023,6 +3023,7 @@ dependencies = [
|
|||||||
"camino",
|
"camino",
|
||||||
"clap",
|
"clap",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
|
"figment",
|
||||||
"httpdate",
|
"httpdate",
|
||||||
"hyper",
|
"hyper",
|
||||||
"ipnetwork",
|
"ipnetwork",
|
||||||
|
@@ -78,6 +78,11 @@ features = ["serde", "clock"]
|
|||||||
version = "4.5.3"
|
version = "4.5.3"
|
||||||
features = ["derive"]
|
features = ["derive"]
|
||||||
|
|
||||||
|
# Configuration loading
|
||||||
|
[workspace.dependencies.figment]
|
||||||
|
version = "0.10.15"
|
||||||
|
features = ["env", "yaml", "test"]
|
||||||
|
|
||||||
# HTTP headers
|
# HTTP headers
|
||||||
[workspace.dependencies.headers]
|
[workspace.dependencies.headers]
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
@@ -17,6 +17,7 @@ axum = "0.6.20"
|
|||||||
camino.workspace = true
|
camino.workspace = true
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
dotenvy = "0.15.7"
|
dotenvy = "0.15.7"
|
||||||
|
figment.workspace = true
|
||||||
httpdate = "1.0.3"
|
httpdate = "1.0.3"
|
||||||
hyper.workspace = true
|
hyper.workspace = true
|
||||||
ipnetwork = "0.20.0"
|
ipnetwork = "0.20.0"
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use camino::Utf8PathBuf;
|
use camino::Utf8PathBuf;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use figment::Figment;
|
||||||
use mas_config::{ConfigurationSection, RootConfig, SyncConfig};
|
use mas_config::{ConfigurationSection, RootConfig, SyncConfig};
|
||||||
use mas_storage::SystemClock;
|
use mas_storage::SystemClock;
|
||||||
use mas_storage_pg::MIGRATOR;
|
use mas_storage_pg::MIGRATOR;
|
||||||
@@ -67,13 +68,13 @@ enum Subcommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Options {
|
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;
|
use Subcommand as SC;
|
||||||
match self.subcommand {
|
match self.subcommand {
|
||||||
SC::Dump { output } => {
|
SC::Dump { output } => {
|
||||||
let _span = info_span!("cli.config.dump").entered();
|
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)?;
|
let config = serde_yaml::to_string(&config)?;
|
||||||
|
|
||||||
if let Some(output) = output {
|
if let Some(output) = output {
|
||||||
@@ -89,8 +90,8 @@ impl Options {
|
|||||||
SC::Check => {
|
SC::Check => {
|
||||||
let _span = info_span!("cli.config.check").entered();
|
let _span = info_span!("cli.config.check").entered();
|
||||||
|
|
||||||
let _config: RootConfig = root.load_config()?;
|
let _config = RootConfig::extract(figment)?;
|
||||||
info!(path = ?root.config, "Configuration file looks good");
|
info!("Configuration file looks good");
|
||||||
}
|
}
|
||||||
|
|
||||||
SC::Generate { output } => {
|
SC::Generate { output } => {
|
||||||
@@ -98,7 +99,7 @@ impl Options {
|
|||||||
|
|
||||||
// XXX: we should disallow SeedableRng::from_entropy
|
// XXX: we should disallow SeedableRng::from_entropy
|
||||||
let rng = rand_chacha::ChaChaRng::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)?;
|
let config = serde_yaml::to_string(&config)?;
|
||||||
|
|
||||||
if let Some(output) = output {
|
if let Some(output) = output {
|
||||||
@@ -112,7 +113,7 @@ impl Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SC::Sync { prune, dry_run } => {
|
SC::Sync { prune, dry_run } => {
|
||||||
let config: SyncConfig = root.load_config()?;
|
let config = SyncConfig::extract(figment)?;
|
||||||
let clock = SystemClock::default();
|
let clock = SystemClock::default();
|
||||||
let encrypter = config.secrets.encrypter();
|
let encrypter = config.secrets.encrypter();
|
||||||
|
|
||||||
|
@@ -14,7 +14,8 @@
|
|||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use mas_config::DatabaseConfig;
|
use figment::Figment;
|
||||||
|
use mas_config::{ConfigurationSection, DatabaseConfig};
|
||||||
use mas_storage_pg::MIGRATOR;
|
use mas_storage_pg::MIGRATOR;
|
||||||
use tracing::{info_span, Instrument};
|
use tracing::{info_span, Instrument};
|
||||||
|
|
||||||
@@ -33,9 +34,9 @@ enum Subcommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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.database.migrate").entered();
|
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?;
|
let mut conn = database_connection_from_config(&config).await?;
|
||||||
|
|
||||||
// Run pending migrations
|
// Run pending migrations
|
||||||
|
@@ -13,8 +13,9 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use figment::Figment;
|
||||||
use hyper::{Response, Uri};
|
use hyper::{Response, Uri};
|
||||||
use mas_config::PolicyConfig;
|
use mas_config::{ConfigurationSection, PolicyConfig};
|
||||||
use mas_handlers::HttpClientFactory;
|
use mas_handlers::HttpClientFactory;
|
||||||
use mas_http::HttpServiceExt;
|
use mas_http::HttpServiceExt;
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
@@ -65,7 +66,7 @@ fn print_headers(parts: &hyper::http::response::Parts) {
|
|||||||
|
|
||||||
impl Options {
|
impl Options {
|
||||||
#[tracing::instrument(skip_all)]
|
#[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;
|
use Subcommand as SC;
|
||||||
let http_client_factory = HttpClientFactory::new();
|
let http_client_factory = HttpClientFactory::new();
|
||||||
match self.subcommand {
|
match self.subcommand {
|
||||||
@@ -120,7 +121,7 @@ impl Options {
|
|||||||
|
|
||||||
SC::Policy => {
|
SC::Policy => {
|
||||||
let _span = info_span!("cli.debug.policy").entered();
|
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");
|
info!("Loading and compiling the policy module");
|
||||||
let policy_factory = policy_factory_from_config(&config).await?;
|
let policy_factory = policy_factory_from_config(&config).await?;
|
||||||
|
|
||||||
|
@@ -19,7 +19,8 @@
|
|||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use mas_config::RootConfig;
|
use figment::Figment;
|
||||||
|
use mas_config::{ConfigurationSection, RootConfig};
|
||||||
use mas_handlers::HttpClientFactory;
|
use mas_handlers::HttpClientFactory;
|
||||||
use mas_http::HttpServiceExt;
|
use mas_http::HttpServiceExt;
|
||||||
use tower::{Service, ServiceExt};
|
use tower::{Service, ServiceExt};
|
||||||
@@ -34,11 +35,11 @@ pub(super) struct Options {}
|
|||||||
|
|
||||||
impl Options {
|
impl Options {
|
||||||
#[allow(clippy::too_many_lines)]
|
#[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();
|
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.");
|
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
|
// We'll need an HTTP client
|
||||||
let http_client_factory = HttpClientFactory::new();
|
let http_client_factory = HttpClientFactory::new();
|
||||||
|
@@ -14,7 +14,8 @@
|
|||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use clap::Parser;
|
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_data_model::{Device, TokenType};
|
||||||
use mas_storage::{
|
use mas_storage::{
|
||||||
compat::{CompatAccessTokenRepository, CompatSessionRepository},
|
compat::{CompatAccessTokenRepository, CompatSessionRepository},
|
||||||
@@ -89,7 +90,7 @@ enum Subcommand {
|
|||||||
|
|
||||||
impl Options {
|
impl Options {
|
||||||
#[allow(clippy::too_many_lines)]
|
#[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;
|
use Subcommand as SC;
|
||||||
let clock = SystemClock::default();
|
let clock = SystemClock::default();
|
||||||
// XXX: we should disallow SeedableRng::from_entropy
|
// XXX: we should disallow SeedableRng::from_entropy
|
||||||
@@ -100,8 +101,8 @@ impl Options {
|
|||||||
let _span =
|
let _span =
|
||||||
info_span!("cli.manage.set_password", user.username = %username).entered();
|
info_span!("cli.manage.set_password", user.username = %username).entered();
|
||||||
|
|
||||||
let database_config: DatabaseConfig = root.load_config()?;
|
let database_config = DatabaseConfig::extract(figment)?;
|
||||||
let passwords_config: PasswordsConfig = root.load_config()?;
|
let passwords_config = PasswordsConfig::extract(figment)?;
|
||||||
|
|
||||||
let mut conn = database_connection_from_config(&database_config).await?;
|
let mut conn = database_connection_from_config(&database_config).await?;
|
||||||
let password_manager = password_manager_from_config(&passwords_config).await?;
|
let password_manager = password_manager_from_config(&passwords_config).await?;
|
||||||
@@ -136,7 +137,7 @@ impl Options {
|
|||||||
)
|
)
|
||||||
.entered();
|
.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 conn = database_connection_from_config(&database_config).await?;
|
||||||
let txn = conn.begin().await?;
|
let txn = conn.begin().await?;
|
||||||
let mut repo = PgRepository::from_conn(txn);
|
let mut repo = PgRepository::from_conn(txn);
|
||||||
@@ -170,7 +171,7 @@ impl Options {
|
|||||||
admin,
|
admin,
|
||||||
device_id,
|
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 mut conn = database_connection_from_config(&database_config).await?;
|
||||||
let txn = conn.begin().await?;
|
let txn = conn.begin().await?;
|
||||||
let mut repo = PgRepository::from_conn(txn);
|
let mut repo = PgRepository::from_conn(txn);
|
||||||
@@ -215,7 +216,7 @@ impl Options {
|
|||||||
|
|
||||||
SC::ProvisionAllUsers => {
|
SC::ProvisionAllUsers => {
|
||||||
let _span = info_span!("cli.manage.provision_all_users").entered();
|
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 conn = database_connection_from_config(&database_config).await?;
|
||||||
let mut txn = conn.begin().await?;
|
let mut txn = conn.begin().await?;
|
||||||
|
|
||||||
@@ -241,7 +242,7 @@ impl Options {
|
|||||||
SC::KillSessions { username, dry_run } => {
|
SC::KillSessions { username, dry_run } => {
|
||||||
let _span =
|
let _span =
|
||||||
info_span!("cli.manage.kill_sessions", user.username = username).entered();
|
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 mut conn = database_connection_from_config(&database_config).await?;
|
||||||
let txn = conn.begin().await?;
|
let txn = conn.begin().await?;
|
||||||
let mut repo = PgRepository::from_conn(txn);
|
let mut repo = PgRepository::from_conn(txn);
|
||||||
@@ -361,7 +362,7 @@ impl Options {
|
|||||||
deactivate,
|
deactivate,
|
||||||
} => {
|
} => {
|
||||||
let _span = info_span!("cli.manage.lock_user", user.username = username).entered();
|
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 mut conn = database_connection_from_config(&config).await?;
|
||||||
let txn = conn.begin().await?;
|
let txn = conn.begin().await?;
|
||||||
let mut repo = PgRepository::from_conn(txn);
|
let mut repo = PgRepository::from_conn(txn);
|
||||||
@@ -393,7 +394,7 @@ impl Options {
|
|||||||
|
|
||||||
SC::UnlockUser { username } => {
|
SC::UnlockUser { username } => {
|
||||||
let _span = info_span!("cli.manage.lock_user", user.username = username).entered();
|
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 mut conn = database_connection_from_config(&config).await?;
|
||||||
let txn = conn.begin().await?;
|
let txn = conn.begin().await?;
|
||||||
let mut repo = PgRepository::from_conn(txn);
|
let mut repo = PgRepository::from_conn(txn);
|
||||||
|
@@ -12,10 +12,12 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use anyhow::Context;
|
|
||||||
use camino::Utf8PathBuf;
|
use camino::Utf8PathBuf;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use mas_config::ConfigurationSection;
|
use figment::{
|
||||||
|
providers::{Env, Format, Yaml},
|
||||||
|
Figment,
|
||||||
|
};
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod database;
|
mod database;
|
||||||
@@ -65,22 +67,23 @@ pub struct Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Options {
|
impl Options {
|
||||||
pub async fn run(mut self) -> anyhow::Result<()> {
|
pub async fn run(self, figment: &Figment) -> anyhow::Result<()> {
|
||||||
use Subcommand as S;
|
use Subcommand as S;
|
||||||
match self.subcommand.take() {
|
match self.subcommand {
|
||||||
Some(S::Config(c)) => c.run(&self).await,
|
Some(S::Config(c)) => c.run(figment).await,
|
||||||
Some(S::Database(c)) => c.run(&self).await,
|
Some(S::Database(c)) => c.run(figment).await,
|
||||||
Some(S::Server(c)) => c.run(&self).await,
|
Some(S::Server(c)) => c.run(figment).await,
|
||||||
Some(S::Worker(c)) => c.run(&self).await,
|
Some(S::Worker(c)) => c.run(figment).await,
|
||||||
Some(S::Manage(c)) => c.run(&self).await,
|
Some(S::Manage(c)) => c.run(figment).await,
|
||||||
Some(S::Templates(c)) => c.run(&self).await,
|
Some(S::Templates(c)) => c.run(figment).await,
|
||||||
Some(S::Debug(c)) => c.run(&self).await,
|
Some(S::Debug(c)) => c.run(figment).await,
|
||||||
Some(S::Doctor(c)) => c.run(&self).await,
|
Some(S::Doctor(c)) => c.run(figment).await,
|
||||||
None => self::server::Options::default().run(&self).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() {
|
let configs = if self.config.is_empty() {
|
||||||
// Read the MAS_CONFIG environment variable
|
// Read the MAS_CONFIG environment variable
|
||||||
std::env::var("MAS_CONFIG")
|
std::env::var("MAS_CONFIG")
|
||||||
@@ -93,7 +96,10 @@ impl Options {
|
|||||||
} else {
|
} else {
|
||||||
self.config.clone()
|
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)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,8 +16,9 @@ use std::{collections::BTreeSet, sync::Arc, time::Duration};
|
|||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use figment::Figment;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use mas_config::AppConfig;
|
use mas_config::{AppConfig, ClientsConfig, ConfigurationSection, UpstreamOAuth2Config};
|
||||||
use mas_handlers::{ActivityTracker, CookieManager, HttpClientFactory, MetadataCache, SiteConfig};
|
use mas_handlers::{ActivityTracker, CookieManager, HttpClientFactory, MetadataCache, SiteConfig};
|
||||||
use mas_listener::{server::Server, shutdown::ShutdownStream};
|
use mas_listener::{server::Server, shutdown::ShutdownStream};
|
||||||
use mas_matrix_synapse::SynapseConnection;
|
use mas_matrix_synapse::SynapseConnection;
|
||||||
@@ -63,9 +64,9 @@ pub(super) struct Options {
|
|||||||
|
|
||||||
impl Options {
|
impl Options {
|
||||||
#[allow(clippy::too_many_lines)]
|
#[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 span = info_span!("cli.run.init").entered();
|
||||||
let config: AppConfig = root.load_config()?;
|
let config = AppConfig::extract(figment)?;
|
||||||
|
|
||||||
if self.migrate {
|
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.");
|
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 {
|
} else {
|
||||||
// Sync the configuration with the database
|
// Sync the configuration with the database
|
||||||
let mut conn = pool.acquire().await?;
|
let mut conn = pool.acquire().await?;
|
||||||
let clients_config = root.load_config()?;
|
let clients_config = ClientsConfig::extract(figment)?;
|
||||||
let upstream_oauth2_config = root.load_config()?;
|
let upstream_oauth2_config = UpstreamOAuth2Config::extract(figment)?;
|
||||||
|
|
||||||
crate::sync::config_sync(
|
crate::sync::config_sync(
|
||||||
upstream_oauth2_config,
|
upstream_oauth2_config,
|
||||||
|
@@ -13,7 +13,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use clap::Parser;
|
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 mas_storage::{Clock, SystemClock};
|
||||||
use rand::SeedableRng;
|
use rand::SeedableRng;
|
||||||
use tracing::info_span;
|
use tracing::info_span;
|
||||||
@@ -33,15 +34,15 @@ enum Subcommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Options {
|
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;
|
use Subcommand as SC;
|
||||||
match self.subcommand {
|
match self.subcommand {
|
||||||
SC::Check => {
|
SC::Check => {
|
||||||
let _span = info_span!("cli.templates.check").entered();
|
let _span = info_span!("cli.templates.check").entered();
|
||||||
|
|
||||||
let template_config: TemplatesConfig = root.load_config()?;
|
let template_config = TemplatesConfig::extract(figment)?;
|
||||||
let branding_config: BrandingConfig = root.load_config()?;
|
let branding_config = BrandingConfig::extract(figment)?;
|
||||||
let matrix_config: MatrixConfig = root.load_config()?;
|
let matrix_config = MatrixConfig::extract(figment)?;
|
||||||
|
|
||||||
let clock = SystemClock::default();
|
let clock = SystemClock::default();
|
||||||
// XXX: we should disallow SeedableRng::from_entropy
|
// XXX: we should disallow SeedableRng::from_entropy
|
||||||
|
@@ -13,7 +13,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use mas_config::AppConfig;
|
use figment::Figment;
|
||||||
|
use mas_config::{AppConfig, ConfigurationSection};
|
||||||
use mas_handlers::HttpClientFactory;
|
use mas_handlers::HttpClientFactory;
|
||||||
use mas_matrix_synapse::SynapseConnection;
|
use mas_matrix_synapse::SynapseConnection;
|
||||||
use mas_router::UrlBuilder;
|
use mas_router::UrlBuilder;
|
||||||
@@ -29,9 +30,9 @@ use crate::util::{database_pool_from_config, mailer_from_config, templates_from_
|
|||||||
pub(super) struct Options {}
|
pub(super) struct Options {}
|
||||||
|
|
||||||
impl 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 span = info_span!("cli.worker.init").entered();
|
||||||
let config: AppConfig = root.load_config()?;
|
let config = AppConfig::extract(figment)?;
|
||||||
|
|
||||||
// Connect to the database
|
// Connect to the database
|
||||||
info!("Connecting to the database");
|
info!("Connecting to the database");
|
||||||
|
@@ -18,7 +18,7 @@ use std::{io::IsTerminal, sync::Arc};
|
|||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use mas_config::TelemetryConfig;
|
use mas_config::{ConfigurationSection, TelemetryConfig};
|
||||||
use sentry_tracing::EventFilter;
|
use sentry_tracing::EventFilter;
|
||||||
use tracing_subscriber::{
|
use tracing_subscriber::{
|
||||||
filter::LevelFilter, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer, Registry,
|
filter::LevelFilter, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer, Registry,
|
||||||
@@ -67,10 +67,13 @@ async fn try_main() -> anyhow::Result<()> {
|
|||||||
// Parse the CLI arguments
|
// Parse the CLI arguments
|
||||||
let opts = self::commands::Options::parse();
|
let opts = self::commands::Options::parse();
|
||||||
|
|
||||||
|
// Load the base configuration files
|
||||||
|
let figment = opts.figment();
|
||||||
|
|
||||||
// Telemetry config could fail to load, but that's probably OK, since the whole
|
// Telemetry config could fail to load, but that's probably OK, since the whole
|
||||||
// config will be loaded afterwards, and crash if there is a problem.
|
// config will be loaded afterwards, and crash if there is a problem.
|
||||||
// Falling back to default.
|
// Falling back to default.
|
||||||
let telemetry_config: TelemetryConfig = opts.load_config().unwrap_or_default();
|
let telemetry_config = TelemetryConfig::extract(&figment).unwrap_or_default();
|
||||||
|
|
||||||
// Setup Sentry
|
// Setup Sentry
|
||||||
let sentry = sentry::init((
|
let sentry = sentry::init((
|
||||||
@@ -126,7 +129,7 @@ async fn try_main() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
// And run the command
|
// And run the command
|
||||||
tracing::trace!(?opts, "Running command");
|
tracing::trace!(?opts, "Running command");
|
||||||
opts.run().await?;
|
opts.run(&figment).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,7 @@ anyhow.workspace = true
|
|||||||
|
|
||||||
camino = { workspace = true, features = ["serde1"] }
|
camino = { workspace = true, features = ["serde1"] }
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
figment = { version = "0.10.15", features = ["env", "yaml", "test"] }
|
figment.workspace = true
|
||||||
ipnetwork = { version = "0.20.0", features = ["serde", "schemars"] }
|
ipnetwork = { version = "0.20.0", features = ["serde", "schemars"] }
|
||||||
schemars.workspace = true
|
schemars.workspace = true
|
||||||
ulid.workspace = true
|
ulid.workspace = true
|
||||||
|
@@ -201,7 +201,10 @@ impl ConfigurationSection for ClientsConfig {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use figment::Jail;
|
use figment::{
|
||||||
|
providers::{Format, Yaml},
|
||||||
|
Figment, Jail,
|
||||||
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -249,7 +252,9 @@ mod tests {
|
|||||||
"#,
|
"#,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let config = ClientsConfig::load_from_file("config.yaml")?;
|
let config = Figment::new()
|
||||||
|
.merge(Yaml::file("config.yaml"))
|
||||||
|
.extract_inner::<ClientsConfig>("clients")?;
|
||||||
|
|
||||||
assert_eq!(config.0.len(), 5);
|
assert_eq!(config.0.len(), 5);
|
||||||
|
|
||||||
|
@@ -164,7 +164,10 @@ impl ConfigurationSection for DatabaseConfig {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use figment::Jail;
|
use figment::{
|
||||||
|
providers::{Format, Yaml},
|
||||||
|
Figment, Jail,
|
||||||
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -179,7 +182,9 @@ mod tests {
|
|||||||
",
|
",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let config = DatabaseConfig::load_from_file("config.yaml")?;
|
let config = Figment::new()
|
||||||
|
.merge(Yaml::file("config.yaml"))
|
||||||
|
.extract_inner::<DatabaseConfig>("database")?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
config.options,
|
config.options,
|
||||||
|
@@ -76,7 +76,10 @@ impl ConfigurationSection for MatrixConfig {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use figment::Jail;
|
use figment::{
|
||||||
|
providers::{Format, Yaml},
|
||||||
|
Figment, Jail,
|
||||||
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -92,10 +95,12 @@ mod tests {
|
|||||||
",
|
",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let config = MatrixConfig::load_from_file("config.yaml")?;
|
let config = Figment::new()
|
||||||
|
.merge(Yaml::file("config.yaml"))
|
||||||
|
.extract_inner::<MatrixConfig>("matrix")?;
|
||||||
|
|
||||||
assert_eq!(config.homeserver, "matrix.org".to_owned());
|
assert_eq!(&config.homeserver, "matrix.org");
|
||||||
assert_eq!(config.secret, "test".to_owned());
|
assert_eq!(&config.secret, "test");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
@@ -12,14 +12,8 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use anyhow::Context;
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use camino::Utf8Path;
|
use figment::{error::Error as FigmentError, Figment};
|
||||||
use figment::{
|
|
||||||
error::Error as FigmentError,
|
|
||||||
providers::{Env, Format, Serialized, Yaml},
|
|
||||||
Figment, Profile,
|
|
||||||
};
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
|
|
||||||
@@ -35,64 +29,13 @@ pub trait ConfigurationSection: Sized + DeserializeOwned + Serialize {
|
|||||||
where
|
where
|
||||||
R: Rng + Send;
|
R: Rng + Send;
|
||||||
|
|
||||||
/// Generate a sample configuration and override it with environment
|
/// Extract configuration from a Figment instance.
|
||||||
/// variables.
|
|
||||||
///
|
|
||||||
/// This is what backs the `config generate` subcommand, allowing to
|
|
||||||
/// programmatically generate a configuration file, e.g.
|
|
||||||
///
|
|
||||||
/// ```sh
|
|
||||||
/// export MAS_OAUTH2_ISSUER=https://example.com/
|
|
||||||
/// export MAS_HTTP_ADDRESS=127.0.0.1:1234
|
|
||||||
/// matrix-authentication-service config generate
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns an error if the configuration could not be generated or if the
|
|
||||||
/// existing configuration could not be loaded
|
|
||||||
async fn load_and_generate<R>(rng: R) -> anyhow::Result<Self>
|
|
||||||
where
|
|
||||||
R: Rng + Send,
|
|
||||||
{
|
|
||||||
let base = Self::generate(rng)
|
|
||||||
.await
|
|
||||||
.context("could not generate configuration")?;
|
|
||||||
|
|
||||||
Figment::new()
|
|
||||||
.merge(Serialized::from(&base, Profile::Default))
|
|
||||||
.merge(Env::prefixed("MAS_").split("_"))
|
|
||||||
.extract_inner(Self::path())
|
|
||||||
.context("could not load configuration")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load configuration from a list of files and environment variables.
|
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if the configuration could not be loaded
|
/// Returns an error if the configuration could not be loaded
|
||||||
fn load_from_files<P>(paths: &[P]) -> Result<Self, FigmentError>
|
fn extract(figment: &Figment) -> Result<Self, FigmentError> {
|
||||||
where
|
figment.extract_inner(Self::path())
|
||||||
P: AsRef<Utf8Path>,
|
|
||||||
{
|
|
||||||
let base = Figment::new().merge(Env::prefixed("MAS_").split("_"));
|
|
||||||
|
|
||||||
paths
|
|
||||||
.iter()
|
|
||||||
.fold(base, |f, path| f.merge(Yaml::file(path.as_ref())))
|
|
||||||
.extract_inner(Self::path())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load configuration from a file and environment variables.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns an error if the configuration could not be loaded
|
|
||||||
fn load_from_file<P>(path: P) -> Result<Self, FigmentError>
|
|
||||||
where
|
|
||||||
P: AsRef<Utf8Path>,
|
|
||||||
{
|
|
||||||
Self::load_from_files(&[path])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate config used in unit tests
|
/// Generate config used in unit tests
|
||||||
|
Reference in New Issue
Block a user