🏡 index : ~doyle/chartered.git

author Jordan Doyle <jordan@doyle.la> 2021-10-20 22:43:47.0 +01:00:00
committer Jordan Doyle <jordan@doyle.la> 2021-10-20 22:57:45.0 +01:00:00
commit
9622c20fe02a11857ebf9b32eb5d12be6e3e858f [patch]
tree
15f805157ed1d1f40e27bf779db41e3c34f87b83
parent
8fc93a55cbdeb87169bab47831f18881f4f4a915
download
9622c20fe02a11857ebf9b32eb5d12be6e3e858f.tar.gz

chartered-git configuration file



Diff

 Cargo.lock                         |  1 +
 chartered-git/.gitignore           |  1 +
 chartered-git/Cargo.toml           |  1 +
 chartered-db/src/lib.rs            | 35 +++++++++++++++++++++++++++++++++++
 chartered-git/src/config.rs        |  9 +++++++++
 chartered-git/src/main.rs          | 40 +++++++++++++++++++++++++++++++++++++++-
 chartered-web/src/config.rs        |  1 +
 chartered-web/src/main.rs          | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 book/src/guide/config-reference.md | 40 ++++++++++++++++++++++++++++++++++++++++
 9 files changed, 157 insertions(+), 23 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 3ea4769..cca583d 100644
--- a/Cargo.lock
+++ a/Cargo.lock
@@ -515,6 +515,7 @@
 "thrussh-keys",
 "tokio",
 "tokio-util",
 "toml",
 "tracing",
 "tracing-subscriber",
 "url",
diff --git a/chartered-git/.gitignore b/chartered-git/.gitignore
new file mode 100644
index 0000000..5b6c096 100644
--- /dev/null
+++ a/chartered-git/.gitignore
@@ -1,0 +1,1 @@
config.toml
diff --git a/chartered-git/Cargo.toml b/chartered-git/Cargo.toml
index 6c9bfff..ecc51b2 100644
--- a/chartered-git/Cargo.toml
+++ a/chartered-git/Cargo.toml
@@ -32,6 +32,7 @@
thrussh-keys = "0.21"
tokio = { version = "1", features = ["full"] }
tokio-util = { version = "0.6", features = ["codec"] }
toml = "0.5"
tracing = "0.1"
tracing-subscriber = "0.2"
url = "2"
diff --git a/chartered-db/src/lib.rs b/chartered-db/src/lib.rs
index b506c72..d58cd18 100644
--- a/chartered-db/src/lib.rs
+++ a/chartered-db/src/lib.rs
@@ -57,8 +57,11 @@
pub type Connection = diesel_tracing::sqlite::InstrumentedSqliteConnection;

#[cfg(feature = "postgres")]
pub type Connection = diesel_tracing::postgres::InstrumentedPostgresConnection;
pub type Connection = diesel_tracing::pg::InstrumentedPgConnection;

#[cfg(all(feature = "sqlite", feature = "postgres"))]
compile_error!("Only one database backend must be enabled using --features [sqlite|postgres]");

#[cfg(not(any(feature = "sqlite", feature = "postgres")))]
compile_error!(
    "At least one database backend must be enabled using `--features [sqlite|postgres]`"
@@ -71,16 +74,42 @@

embed_migrations!();

pub fn init() -> Result<ConnectionPool> {
    let pool = Pool::new(ConnectionManager::new("chartered.db"))?;
pub fn init(connection_uri: &str) -> Result<ConnectionPool> {
    let connection_uri = parse_connection_uri(connection_uri)?;
    let pool = Pool::new(ConnectionManager::new(connection_uri))?;

    embedded_migrations::run_with_output(&pool.get()?, &mut std::io::stdout())?;

    Ok(Arc::new(pool))
}

#[cfg(feature = "sqlite")]
pub fn parse_connection_uri(connection_uri: &str) -> Result<&str> {
    if connection_uri.starts_with("sqlite://") {
        Ok(connection_uri.trim_start_matches("sqlite://"))
    } else {
        Err(Error::SqliteConnectionUri)
    }
}

#[cfg(feature = "postgres")]
pub fn parse_connection_uri(connection_uri: &str) -> Result<&str> {
    if connection_uri.starts_with("postgres://") {
        Ok(connection_uri)
    } else {
        Err(Error::PostgresConnectionUri)
    }
}

#[derive(Error, Display, Debug)]
pub enum Error {
    /// connection_uri must be in the format `sqlite:///path/to/file.db` or `sqlite://:memory:`

    SqliteConnectionUri,
    /**

     * connection_uri must be a postgres connection uri as described in:
     * https://www.postgresql.org/docs/9.4/libpq-connect.html#LIBPQ-CONNSTRING
     */
    PostgresConnectionUri,
    /// Failed to initialise to database connection pool

    Connection(#[from] diesel::r2d2::PoolError),
    /// Failed to run migrations to bring database schema up-to-date: {0}

diff --git a/chartered-git/src/config.rs b/chartered-git/src/config.rs
new file mode 100644
index 0000000..8103e91 100644
--- /dev/null
+++ a/chartered-git/src/config.rs
@@ -1,0 +1,9 @@
use serde::Deserialize;
use std::net::SocketAddr;

#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
    pub bind_address: SocketAddr,
    pub database_uri: String,
}
diff --git a/chartered-git/src/main.rs b/chartered-git/src/main.rs
index a6a1607..d7d4675 100644
--- a/chartered-git/src/main.rs
+++ a/chartered-git/src/main.rs
@@ -1,6 +1,7 @@
#![deny(clippy::pedantic)]
#![deny(rust_2018_idioms)]
mod command_handlers;
mod config;
mod generators;
mod tree;

@@ -19,8 +20,9 @@

use arrayvec::ArrayVec;
use bytes::BytesMut;
use clap::Parser;
use futures::future::Future;
use std::{fmt::Write, pin::Pin, sync::Arc};
use std::{fmt::Write, path::PathBuf, pin::Pin, sync::Arc};
use thrussh::{
    server::{self, Auth, Session},
    ChannelId, CryptoVec,
@@ -30,24 +32,48 @@
use tracing::{debug, error, info, warn, Instrument};
use url::Url;

#[derive(Parser)]
#[clap(version = clap::crate_version!(), author = clap::crate_authors!())]
pub struct Opts {
    #[clap(short, long, parse(from_occurrences))]
    verbose: i32,
    #[clap(short, long)]
    config: PathBuf,
}

#[tokio::main]
#[allow(clippy::semicolon_if_nothing_returned)] // broken clippy lint
async fn main() {
async fn main() -> anyhow::Result<()> {
    let opts: Opts = Opts::parse();

    std::env::set_var(
        "RUST_LOG",
        match opts.verbose {
            1 => "debug",
            2 => "trace",
            _ => "info",
        },
    );

    let config: config::Config = toml::from_slice(&std::fs::read(&opts.config)?)?;

    tracing_subscriber::fmt::init();

    let config = Arc::new(thrussh::server::Config {
    let trussh_config = Arc::new(thrussh::server::Config {
        methods: thrussh::MethodSet::PUBLICKEY,
        keys: vec![key::KeyPair::generate_ed25519().unwrap()],
        ..thrussh::server::Config::default()
    });

    let server = Server {
        db: chartered_db::init().unwrap(),
        db: chartered_db::init(&config.database_uri)?,
    };

    info!("SSH server listening on {}", config.bind_address);

    thrussh::server::run(trussh_config, &config.bind_address.to_string(), server).await?;

    thrussh::server::run(config, "127.0.0.1:2233", server)
        .await
        .unwrap();
    Ok(())
}

#[derive(Clone)]
diff --git a/chartered-web/src/config.rs b/chartered-web/src/config.rs
index e6fcd0e..20fbacb 100644
--- a/chartered-web/src/config.rs
+++ a/chartered-web/src/config.rs
@@ -20,6 +20,7 @@
#[serde(deny_unknown_fields)]
pub struct Config {
    pub bind_address: SocketAddr,
    pub database_uri: String,
    pub storage_uri: String,
    pub auth: AuthConfig,
    #[serde(deserialize_with = "deserialize_encryption_key")]
diff --git a/chartered-web/src/main.rs b/chartered-web/src/main.rs
index f13872c..7ab6c49 100644
--- a/chartered-web/src/main.rs
+++ a/chartered-web/src/main.rs
@@ -12,10 +12,11 @@
    AddExtensionLayer, Router,
};
use clap::Parser;
use std::path::PathBuf;
use std::sync::Arc;
use std::{fmt::Formatter, path::PathBuf, sync::Arc};
use thiserror::Error;
use tower::ServiceBuilder;
use tower_http::cors::{Any, CorsLayer};
use tracing::info;

#[derive(Parser)]
#[clap(version = clap::crate_version!(), author = clap::crate_authors!())]
@@ -56,15 +57,24 @@

#[tokio::main]
#[allow(clippy::semicolon_if_nothing_returned)] // lint breaks with tokio::main
async fn main() {
async fn main() -> Result<(), InitError> {
    let opts: Opts = Opts::parse();
    let config: config::Config = toml::from_slice(&std::fs::read(&opts.config).unwrap()).unwrap();

    let bind_address = config.bind_address;
    std::env::set_var(
        "RUST_LOG",
        match opts.verbose {
            1 => "debug",
            2 => "trace",
            _ => "info",
        },
    );

    let config: config::Config = toml::from_slice(&std::fs::read(&opts.config)?)?;

    tracing_subscriber::fmt::init();

    let pool = chartered_db::init().unwrap();
    let bind_address = config.bind_address;
    let pool = chartered_db::init(&config.database_uri)?;

    let middleware_stack = ServiceBuilder::new()
        .layer_fn(middleware::logging::LoggingMiddleware)
@@ -107,15 +117,39 @@
        )
        .layer(AddExtensionLayer::new(pool))
        .layer(AddExtensionLayer::new(Arc::new(
            config.create_oidc_clients().await.unwrap(),
            config.create_oidc_clients().await?,
        )))
        .layer(AddExtensionLayer::new(Arc::new(
            config.get_file_system().await.unwrap(),
            config.get_file_system().await?,
        )))
        .layer(AddExtensionLayer::new(Arc::new(config)));

    info!("HTTP server listening on {}", bind_address);

    axum::Server::bind(&bind_address)
        .serve(app.into_make_service_with_connect_info::<std::net::SocketAddr, _>())
        .await
        .unwrap();
        .map_err(|e| InitError::ServerSpawn(Box::new(e)))?;

    Ok(())
}

#[derive(Error)]
pub enum InitError {
    #[error("Failed to read configuration: {0}")]
    ConfigRead(#[from] std::io::Error),
    #[error("Failed to parse configuration: {0}")]
    ConfigParse(#[from] toml::de::Error),
    #[error("Configuration error: {0}")]
    Config(#[from] config::Error),
    #[error("Database error: {0}")]
    Database(#[from] chartered_db::Error),
    #[error("Failed to spawn HTTP server: {0}")]
    ServerSpawn(Box<dyn std::error::Error>),
}

impl std::fmt::Debug for InitError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self)
    }
}
diff --git a/book/src/guide/config-reference.md b/book/src/guide/config-reference.md
index 681d2aa..a6e9bf8 100644
--- a/book/src/guide/config-reference.md
+++ a/book/src/guide/config-reference.md
@@ -1,14 +1,41 @@
# Configuration Reference

An exhaustive list of all configuration values in chartered.

## chartered-web
Configuration files are written in the TOML format, with simple key-value pairs inside of sections (tables). The following
is a quick overview of all settings, with detailed descriptions found below

## chartered-git

### Configuration format

Configuration files are written in the TOML format, with simple key-value pairs inside of sections (tables). The following
is a quick overview of all settings, with detailed descriptions found below
```toml

bind_address = "127.0.0.1:2233"
database_uri = "postgres://user:password@localhost/chartered" # can also be `sqlite://`
```


### Configuration keys

#### `bind_address`
- Type: string

The IP address and port the web server should be bound to.

#### `database_uri`
- Type: string

A connection string for the backing database, either `postgres` or `sqlite`, either in the
format of `postgres://user:password@localhost/chartered` (a [postgres connection URI][pg-uri]),
`sqlite:///path/to/chartered.db` or `sqlite://:memory:`.

[pg-uri]: https://www.postgresql.org/docs/9.4/libpq-connect.html#LIBPQ-CONNSTRING

---


## chartered-web

### Configuration format

```toml

bind_address = "127.0.0.1:8080"
database_uri = "postgres://user:password@localhost/chartered" # can also be `sqlite://`
@@ -33,8 +60,13 @@
The IP address and port the web server should be bound to.

#### `database_uri`
- Type: string

A connection string for the backing database, either `postgres` or `sqlite`, either in the
format of `postgres://user:password@localhost/chartered` (a [postgres connection URI][pg-uri]),
`sqlite:///path/to/chartered.db` or `sqlite://:memory:`.

A JDBC-like connection string for the backing database, either `postgres` or `sqlite`.
[pg-uri]: https://www.postgresql.org/docs/9.4/libpq-connect.html#LIBPQ-CONNSTRING

#### `storage_uri`
- Type: string