split CLI in multiple commands
- `config` to view & check config files - `database` for DB-related ops - `server` to run the server
This commit is contained in:
115
Cargo.lock
generated
115
Cargo.lock
generated
@@ -397,6 +397,17 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
@@ -645,6 +656,38 @@ dependencies = [
|
||||
"generic-array 0.14.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.0.0-beta.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"os_str_bytes",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "3.0.0-beta.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "370f715b81112975b1b69db93e0b56ea4cd4e5002ac43b2da8474106a54096a1"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "1.2.2"
|
||||
@@ -1355,6 +1398,16 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "1.0.3"
|
||||
@@ -1497,6 +1550,7 @@ dependencies = [
|
||||
"async-std",
|
||||
"async-trait",
|
||||
"chrono",
|
||||
"clap",
|
||||
"csrf",
|
||||
"data-encoding",
|
||||
"figment",
|
||||
@@ -1505,6 +1559,7 @@ dependencies = [
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_with",
|
||||
"serde_yaml",
|
||||
"sqlx",
|
||||
"tera",
|
||||
"thiserror",
|
||||
@@ -1611,6 +1666,12 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85"
|
||||
|
||||
[[package]]
|
||||
name = "parking"
|
||||
version = "2.0.0"
|
||||
@@ -1827,6 +1888,30 @@ version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.19"
|
||||
@@ -2616,6 +2701,24 @@ dependencies = [
|
||||
"unic-segment",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.26"
|
||||
@@ -2923,6 +3026,12 @@ version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
@@ -2981,6 +3090,12 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.3"
|
||||
|
||||
@@ -6,6 +6,7 @@ edition = "2018"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
clap = "3.0.0-beta.2"
|
||||
serde = { version = "1.0.126", features = ["derive"] }
|
||||
async-std = { version = "1.9.0", features = ["attributes"] }
|
||||
tide = "0.16.0"
|
||||
@@ -26,3 +27,4 @@ mime = "0.3.16"
|
||||
sqlx = { version = "0.5.5", features = ["runtime-async-std-rustls", "postgres", "migrate", "chrono"] }
|
||||
serde_with = { version = "1.9.4", features = ["hex", "chrono"] }
|
||||
schemars = { version = "0.8.3", features = ["url", "chrono"] }
|
||||
serde_yaml = "0.8.17"
|
||||
|
||||
65
matrix-authentication-service/src/cli/config.rs
Normal file
65
matrix-authentication-service/src/cli/config.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use clap::Clap;
|
||||
use schemars::schema_for;
|
||||
use tracing::info;
|
||||
|
||||
use super::RootCommand;
|
||||
use crate::config::RootConfig;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
pub(super) struct ConfigCommand {
|
||||
#[clap(subcommand)]
|
||||
subcommand: ConfigSubcommand,
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
enum ConfigSubcommand {
|
||||
/// Dump the current config as YAML
|
||||
Dump,
|
||||
|
||||
/// Print the JSON Schema that validates configuration files
|
||||
Schema,
|
||||
|
||||
/// Check a config file
|
||||
Check,
|
||||
}
|
||||
|
||||
impl ConfigCommand {
|
||||
pub async fn run(&self, root: &RootCommand) -> anyhow::Result<()> {
|
||||
use ConfigSubcommand as SC;
|
||||
match &self.subcommand {
|
||||
SC::Dump => {
|
||||
let config = root.load_config()?;
|
||||
|
||||
serde_yaml::to_writer(std::io::stdout(), &config)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
SC::Schema => {
|
||||
let schema = schema_for!(RootConfig);
|
||||
|
||||
serde_yaml::to_writer(std::io::stdout(), &schema)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
SC::Check => {
|
||||
root.load_config()?;
|
||||
info!(path = ?root.config, "Configuration file looks good");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
46
matrix-authentication-service/src/cli/database.rs
Normal file
46
matrix-authentication-service/src/cli/database.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use anyhow::Context;
|
||||
use clap::Clap;
|
||||
|
||||
use super::RootCommand;
|
||||
use crate::storage::MIGRATOR;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
pub(super) struct DatabaseCommand {
|
||||
#[clap(subcommand)]
|
||||
subcommand: DatabaseSubcommand,
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
enum DatabaseSubcommand {
|
||||
/// Run database migrations
|
||||
Migrate,
|
||||
}
|
||||
|
||||
impl DatabaseCommand {
|
||||
pub async fn run(&self, root: &RootCommand) -> anyhow::Result<()> {
|
||||
let config = root.load_config()?;
|
||||
let pool = config.database.connect().await?;
|
||||
|
||||
// Run pending migrations
|
||||
MIGRATOR
|
||||
.run(&pool)
|
||||
.await
|
||||
.context("could not run migrations")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
63
matrix-authentication-service/src/cli/mod.rs
Normal file
63
matrix-authentication-service/src/cli/mod.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Context;
|
||||
use clap::Clap;
|
||||
|
||||
use self::{config::ConfigCommand, database::DatabaseCommand, server::ServerCommand};
|
||||
use crate::config::RootConfig;
|
||||
|
||||
mod config;
|
||||
mod database;
|
||||
mod server;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
enum Subcommand {
|
||||
/// Configuration-related commands
|
||||
Config(ConfigCommand),
|
||||
|
||||
/// Manage the database
|
||||
Database(DatabaseCommand),
|
||||
|
||||
/// Runs the web server
|
||||
Server(ServerCommand),
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
pub struct RootCommand {
|
||||
/// Path to the configuration file
|
||||
#[clap(short, long, global = true, default_value = "config.yaml")]
|
||||
config: PathBuf,
|
||||
|
||||
#[clap(subcommand)]
|
||||
subcommand: Option<Subcommand>,
|
||||
}
|
||||
|
||||
impl RootCommand {
|
||||
pub async fn run(&self) -> anyhow::Result<()> {
|
||||
use Subcommand as S;
|
||||
match &self.subcommand {
|
||||
Some(S::Config(c)) => c.run(self).await,
|
||||
Some(S::Database(c)) => c.run(self).await,
|
||||
Some(S::Server(c)) => c.run(self).await,
|
||||
None => ServerCommand::default().run(self).await,
|
||||
}
|
||||
}
|
||||
|
||||
fn load_config(&self) -> anyhow::Result<RootConfig> {
|
||||
RootConfig::load(&self.config).context("Could not load configuration")
|
||||
}
|
||||
}
|
||||
52
matrix-authentication-service/src/cli/server.rs
Normal file
52
matrix-authentication-service/src/cli/server.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use anyhow::Context;
|
||||
use clap::Clap;
|
||||
|
||||
use super::RootCommand;
|
||||
use crate::state::State;
|
||||
|
||||
#[derive(Clap, Debug, Default)]
|
||||
pub(super) struct ServerCommand;
|
||||
|
||||
impl ServerCommand {
|
||||
pub async fn run(&self, root: &RootCommand) -> anyhow::Result<()> {
|
||||
let config = root.load_config()?;
|
||||
|
||||
// Connect to the database
|
||||
let pool = config.database.connect().await?;
|
||||
|
||||
// Load and compile the templates
|
||||
let templates = crate::templates::load().context("could not load templates")?;
|
||||
|
||||
// Create the shared state
|
||||
let state = State::new(config, templates, pool);
|
||||
state
|
||||
.storage()
|
||||
.load_static_clients(&state.config().oauth2.clients)
|
||||
.await;
|
||||
|
||||
// Start the server
|
||||
let address = state.config().http.address.clone();
|
||||
let mut app = tide::with_state(state);
|
||||
app.with(tide_tracing::TraceMiddleware::new());
|
||||
crate::handlers::install(&mut app);
|
||||
app.listen(address)
|
||||
.await
|
||||
.context("could not start server")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Context;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::skip_serializing_none;
|
||||
@@ -78,7 +79,7 @@ pub struct DatabaseConfig {
|
||||
|
||||
impl DatabaseConfig {
|
||||
#[tracing::instrument(err)]
|
||||
pub async fn connect(&self) -> Result<PgPool, sqlx::Error> {
|
||||
pub async fn connect(&self) -> anyhow::Result<PgPool> {
|
||||
PgPoolOptions::new()
|
||||
.max_connections(self.max_connections)
|
||||
.min_connections(self.min_connections)
|
||||
@@ -87,5 +88,6 @@ impl DatabaseConfig {
|
||||
.max_lifetime(self.max_lifetime)
|
||||
.connect(&self.uri)
|
||||
.await
|
||||
.context("could not connect to the database")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use figment::{
|
||||
error::Error as FigmentError,
|
||||
providers::{Env, Format, Yaml},
|
||||
@@ -51,10 +53,13 @@ pub struct RootConfig {
|
||||
}
|
||||
|
||||
impl RootConfig {
|
||||
pub fn load() -> Result<RootConfig, FigmentError> {
|
||||
pub fn load<P>(path: P) -> Result<RootConfig, FigmentError>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
Figment::new()
|
||||
.merge(Env::prefixed("MAS_").split("_"))
|
||||
.merge(Yaml::file("config.yaml"))
|
||||
.merge(Yaml::file(path))
|
||||
.extract()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,10 @@
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
use anyhow::Context;
|
||||
use tracing::{info_span, Instrument};
|
||||
use clap::Clap;
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry};
|
||||
|
||||
mod cli;
|
||||
mod config;
|
||||
mod csrf;
|
||||
mod handlers;
|
||||
@@ -29,10 +30,10 @@ mod state;
|
||||
mod storage;
|
||||
mod templates;
|
||||
|
||||
use self::{config::RootConfig, state::State, storage::MIGRATOR};
|
||||
use self::cli::RootCommand;
|
||||
|
||||
#[async_std::main]
|
||||
async fn main() -> tide::Result<()> {
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
// Setup logging & tracing
|
||||
let fmt_layer = tracing_subscriber::fmt::layer();
|
||||
let filter_layer = EnvFilter::try_from_default_env().or_else(|_| EnvFilter::try_new("info"))?;
|
||||
@@ -42,41 +43,9 @@ async fn main() -> tide::Result<()> {
|
||||
.try_init()
|
||||
.context("could not initialize logging")?;
|
||||
|
||||
// Loading the config
|
||||
let config = RootConfig::load().context("could not load config")?;
|
||||
// Parse the CLI arguments
|
||||
let opts = RootCommand::parse();
|
||||
|
||||
// Connect to the database
|
||||
let pool = config
|
||||
.database
|
||||
.connect()
|
||||
.await
|
||||
.context("could not connect to database")?;
|
||||
|
||||
// Load and compile the templates
|
||||
let templates = self::templates::load().context("could not load templates")?;
|
||||
|
||||
// Create the shared state
|
||||
let state = State::new(config, templates, pool);
|
||||
state
|
||||
.storage()
|
||||
.load_static_clients(&state.config().oauth2.clients)
|
||||
.await;
|
||||
|
||||
// Run pending migrations
|
||||
// TODO: make this a separate command
|
||||
MIGRATOR
|
||||
.run(state.storage().pool())
|
||||
.instrument(info_span!("migrations"))
|
||||
.await
|
||||
.context("could not run migrations")?;
|
||||
|
||||
// Start the server
|
||||
let address = state.config().http.address.clone();
|
||||
let mut app = tide::with_state(state);
|
||||
app.with(tide_tracing::TraceMiddleware::new());
|
||||
self::handlers::install(&mut app);
|
||||
app.listen(address)
|
||||
.await
|
||||
.context("could not start server")?;
|
||||
Ok(())
|
||||
// And run the command
|
||||
opts.run().await
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user