mirror of https://github.com/mastodon/flodgatt
Postgres config (#70)
* Add logging for known env variables * Update postgres config to match other configs * Update README and bump version to 0.4.2
This commit is contained in:
parent
4a2d08c693
commit
0de3d3c484
|
@ -404,7 +404,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "flodgatt"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dotenv 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "flodgatt"
|
||||
description = "A blazingly fast drop-in replacement for the Mastodon streaming api server"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
authors = ["Daniel Long Sockwell <daniel@codesections.com", "Julian Laubstein <contact@julianlaubstein.de>"]
|
||||
edition = "2018"
|
||||
|
||||
|
|
79
README.md
79
README.md
|
@ -5,41 +5,74 @@ Flóðgátt
|
|||
|
||||
A blazingly fast drop-in replacement for the Mastodon streaming API server.
|
||||
|
||||
> **Current status:** This server is currently a **work in progress**. However, it is now testable and, if configured properly, would theoretically be usable in production—though production use is not advisable until we have completed further testing. I would greatly appreciate any testing, bug reports, or other feedback you could provide.
|
||||
> **Current status:** This server is currently a **work in progress**. However, it is now testable
|
||||
> and, if configured properly, would theoretically be usable in production—though production use
|
||||
> is not advisable until we have completed further testing. I would greatly appreciate any
|
||||
> testing, bug reports, or other feedback you could provide.
|
||||
|
||||
## Installation
|
||||
|
||||
Starting from version 0.3, Flóðgátt can be installed for Linux by installing the pre-built binaries released on GitHub. Simply download the binary (extracting it if necessary), set it to executable (`chmod +x`) and run it. Note that you will likely need to configure the Postgres connection before you can successfully connect.
|
||||
Starting from version 0.3, Flóðgátt can be installed for Linux by installing the pre-built
|
||||
binaries released on GitHub. Simply download the binary (extracting it if necessary), set it to
|
||||
executable (`chmod +x`) and run it. Note that you will likely need to configure the Postgres
|
||||
connection before you can successfully connect.
|
||||
|
||||
### Configuration Examples
|
||||
|
||||
If you are running Mastodon with its [standard Development settings](https://docs.joinmastodon.org/development/overview/), then you should be able to run `flodgatt` without any configuration. (You will, of course, need to ensure that the Node streaming server is not running at the same time as Flodgatt. If you normally run the development servers with `foreman start`, you should edit the `Procfile.dev` file to remove the line that starts the Node server). You will likely wish to use the environmental variable `RUST_LOG=warn` to enable debugging warnings.
|
||||
If you are running Mastodon with its [standard Development
|
||||
settings](https://docs.joinmastodon.org/dev/setup/), then you should be able to run `flodgatt`
|
||||
without any configuration. (You will, of course, need to ensure that the Node streaming server is
|
||||
not running at the same time as Flodgatt. If you normally run the development servers with
|
||||
`foreman start`, you should edit the `Procfile.dev` file to remove the line that starts the Node
|
||||
server. To run `flodgatt` with a production instance of Mastodon, you should ensure that the
|
||||
`mastodon-streaming` systemd service is not running.)
|
||||
|
||||
If you are running Mastodon with its standard Production settings and connect to Postgres with the Ident authentication method, then you can use the following procedure to launch Flodgatt.
|
||||
* Change to the user that satisfies the Ident requirement (typically "mastodon" with default settints). For example: `su mastodon`
|
||||
* Use environmental variables to set the user, database, and host names. For example: `DB_NAME="mastodon_production" DB_USER="mastodon" DB_HOST="/var/run/postgresql" RUST_LOG=warn flodgatt`
|
||||
You will likely wish to use the environmental variable `RUST_LOG=warn` to enable debugging warnings.
|
||||
|
||||
If you are running Mastodon with its standard Production settings and connect to Postgres with the
|
||||
Ident authentication method, then you can use the following procedure to launch Flodgatt.
|
||||
* Change to the user that satisfies the Ident requirement (typically "mastodon" with default
|
||||
settints). For example: `su mastodon`
|
||||
* Use environmental variables to set the user, database, and host names. For example:
|
||||
`DB_NAME="mastodon_production" DB_USER="mastodon" DB_HOST="/var/run/postgresql" RUST_LOG=warn
|
||||
flodgatt`
|
||||
|
||||
If you have any difficulty connecting, note that, if run with `RUST_LOG=warn` Flodgatt will print the parsed configuration variables it thinks that you passed to it. You can use this info to debug the connection.
|
||||
If you have any difficulty connecting, note that, if run with `RUST_LOG=warn` Flodgatt will print
|
||||
both the environmental variables it received and the parsed configuration variables it generated
|
||||
from those environmental variables. You can use this info to debug the connection.
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
The streaming server will eventually uses the same environment variables as the rest of Mastodon, and currently uses a subset of those variables. Supported variables are listed in `/src/config.rs`. Supported environmental variables either be passed to Flóðgátt at runtime or through a `.env` file.
|
||||
The streaming server will eventually uses the same environment variables as the rest of Mastodon,
|
||||
and currently uses a subset of those variables. Supported variables are listed in
|
||||
`/src/config.rs`. Supported environmental variables either be passed to Flóðgátt at runtime or
|
||||
through a `.env` file.
|
||||
|
||||
Note that the default values for the `postgres` connection do not correspond to those typically used in production. Thus, you will need to configure the connection either env vars or a `.env` file if you intend to connect Flóðgátt to a production database.
|
||||
Note that the default values for the `postgres` connection do not correspond to those typically
|
||||
used in production. Thus, you will need to configure the connection either env vars or a `.env`
|
||||
file if you intend to connect Flóðgátt to a production database.
|
||||
|
||||
Additionally, note that connecting Flóðgátt to Postgres with the `ident` method requires running Flóðgátt as the user who owns the mastodon database (typically `mastodon`).
|
||||
Additionally, note that connecting Flóðgátt to Postgres with the `ident` method requires running
|
||||
Flóðgátt as the user who owns the mastodon database (typically `mastodon`).
|
||||
|
||||
## Building from source
|
||||
|
||||
Installing from source requires the Rust toolchain. Clone this repository and run `cargo build` (to build the server), or `cargo build --release` (to build the server with release optimizations).
|
||||
Installing from source requires the Rust toolchain. Clone this repository and run `cargo build`
|
||||
(to build the server), or `cargo build --release` (to build the server with release
|
||||
optimizations).
|
||||
|
||||
### Running the built server
|
||||
|
||||
You can run the server with `cargo run`. Alternatively, if you built the sever using `cargo build` or `cargo build --release`, you can run the executable produced in the `target/build/debug` folder or the `target/build/release` folder.
|
||||
You can run the server with `cargo run`. Alternatively, if you built the sever using `cargo build`
|
||||
or `cargo build --release`, you can run the executable produced in the `target/build/debug` folder
|
||||
or the `target/build/release` folder.
|
||||
|
||||
### Building documentation
|
||||
|
||||
Build documentation with `cargo doc --open`, which will build the Markdown docs and open them in your browser. Please consult those docs for a detailed description of the code structure/organization. The documentation also contains additional notes about data flow and options for configuration.
|
||||
Build documentation with `cargo doc --open`, which will build the Markdown docs and open them in
|
||||
your browser. Please consult those docs for a detailed description of the code
|
||||
structure/organization. The documentation also contains additional notes about data flow and
|
||||
options for configuration.
|
||||
|
||||
### Testing
|
||||
|
||||
|
@ -47,17 +80,29 @@ You can run basic unit tests with `cargo test`.
|
|||
|
||||
### Manual testing
|
||||
|
||||
Once the streaming server is running, you can also test it manually. You can test it using a browser connected to the relevant Mastodon development server. Or you can test the SSE endpoints with `curl`, PostMan, or any other HTTP client. Similarly, you can test the WebSocket endpoints with `websocat` or any other WebSocket client.
|
||||
Once the streaming server is running, you can also test it manually. You can test it using a
|
||||
browser connected to the relevant Mastodon development server. Or you can test the SSE endpoints
|
||||
with `curl`, PostMan, or any other HTTP client. Similarly, you can test the WebSocket endpoints
|
||||
with `websocat` or any other WebSocket client.
|
||||
|
||||
### Memory/CPU usage
|
||||
|
||||
Note that memory usage is higher when running the development version of the streaming server (the one generated with `cargo run` or `cargo build`). If you are interested in measuring RAM or CPU usage, you should likely run `cargo build --release` and test the release version of the executable.
|
||||
Note that memory usage is higher when running the development version of the streaming server (the
|
||||
one generated with `cargo run` or `cargo build`). If you are interested in measuring RAM or CPU
|
||||
usage, you should likely run `cargo build --release` and test the release version of the
|
||||
executable.
|
||||
|
||||
### Load testing
|
||||
|
||||
I have not yet found a good way to test the streaming server under load. I have experimented with using `artillery` or other load-testing utilities. However, every utility I am familiar with or have found is built around either HTTP requests or WebSocket connections in which the client sends messages. I have not found a good solution to test receiving SSEs or WebSocket connections where the client does not transmit data after establishing the connection. If you are aware of a good way to do load testing, please let me know.
|
||||
I have not yet found a good way to test the streaming server under load. I have experimented with
|
||||
using `artillery` or other load-testing utilities. However, every utility I am familiar with or
|
||||
have found is built around either HTTP requests or WebSocket connections in which the client sends
|
||||
messages. I have not found a good solution to test receiving SSEs or WebSocket connections where
|
||||
the client does not transmit data after establishing the connection. If you are aware of a good
|
||||
way to do load testing, please let me know.
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
Issues and pull requests are welcome. Flóðgátt is governed by the same Code of Conduct as Mastodon as a whole.
|
||||
Issues and pull requests are welcome. Flóðgátt is governed by the same Code of Conduct as Mastodon
|
||||
as a whole.
|
||||
|
|
|
@ -25,7 +25,7 @@ impl DeploymentConfig<'_> {
|
|||
cors: Cors::default(),
|
||||
};
|
||||
cfg.env = cfg.env.maybe_update(env.get("RUST_ENV"));
|
||||
log::info!("Using deployment configuration:\n {:#?}", &cfg);
|
||||
log::warn!("Using deployment configuration:\n {:#?}", &cfg);
|
||||
cfg
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::{
|
|||
use strum_macros::{EnumString, EnumVariantNames};
|
||||
|
||||
from_env_var!(
|
||||
/// The current environment, which controls what file to read other ENV vars from
|
||||
/// The current environment, which controls what file to read other ENV vars from
|
||||
let name = Env;
|
||||
let default: EnvInner = EnvInner::Development;
|
||||
let (env_var, allowed_values) = ("RUST_ENV", format!("one of: {:?}", EnvInner::variants()));
|
||||
|
@ -29,7 +29,7 @@ from_env_var!(
|
|||
/// How verbosely Flodgatt should log messages
|
||||
let name = LogLevel;
|
||||
let default: LogLevelInner = LogLevelInner::Warn;
|
||||
let (env_var, allowed_values) = ("RUST_LOG", "a valid address (e.g., 127.0.0.1)".to_string());
|
||||
let (env_var, allowed_values) = ("RUST_LOG", format!("one of: {:?}", LogLevelInner::variants()));
|
||||
let from_str = |s| LogLevelInner::from_str(s).ok();
|
||||
);
|
||||
from_env_var!(
|
||||
|
@ -46,7 +46,7 @@ from_env_var!(
|
|||
/// The time between replies sent via WebSocket
|
||||
let name = WsInterval;
|
||||
let default: Duration = Duration::from_millis(100);
|
||||
let (env_var, allowed_values) = ("WS_FREQ", "a valid Unix Socket".to_string());
|
||||
let (env_var, allowed_values) = ("WS_FREQ", "a number of milliseconds".to_string());
|
||||
let from_str = |s| s.parse().map(Duration::from_millis).ok();
|
||||
);
|
||||
from_env_var!(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
mod deployment_cfg;
|
||||
mod deployment_cfg_types;
|
||||
mod postgres_cfg;
|
||||
mod postgres_cfg_types;
|
||||
mod redis_cfg;
|
||||
mod redis_cfg_types;
|
||||
pub use self::{
|
||||
|
@ -9,8 +10,7 @@ pub use self::{
|
|||
redis_cfg::RedisConfig,
|
||||
redis_cfg_types::{RedisInterval, RedisNamespace},
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use url::Url;
|
||||
use std::{collections::HashMap, fmt};
|
||||
|
||||
pub struct EnvVar(pub HashMap<String, String>);
|
||||
impl std::ops::Deref for EnvVar {
|
||||
|
@ -19,41 +19,58 @@ impl std::ops::Deref for EnvVar {
|
|||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for EnvVar {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
impl EnvVar {
|
||||
fn update_with_url(mut self, url_str: &str) -> Self {
|
||||
let url = Url::parse(url_str).unwrap();
|
||||
let none_if_empty = |s: String| if s.is_empty() { None } else { Some(s) };
|
||||
|
||||
self.maybe_add_env_var("REDIS_PORT", url.port());
|
||||
self.maybe_add_env_var("REDIS_PASSWORD", url.password());
|
||||
self.maybe_add_env_var("REDIS_USERNAME", none_if_empty(url.username().to_string()));
|
||||
self.maybe_add_env_var("REDIS_DB", none_if_empty(url.path()[1..].to_string()));
|
||||
for (k, v) in url.query_pairs().into_owned() {
|
||||
match k.to_string().as_str() {
|
||||
"password" => self.maybe_add_env_var("REDIS_PASSWORD", Some(v.to_string())),
|
||||
"db" => self.maybe_add_env_var("REDIS_DB", Some(v.to_string())),
|
||||
_ => crate::err::die_with_msg(format!(
|
||||
r"Unsupported parameter {} in REDIS_URL.
|
||||
Flodgatt supports only `password` and `db` parameters.",
|
||||
k
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
self
|
||||
pub fn new(vars: HashMap<String, String>) -> Self {
|
||||
Self(vars)
|
||||
}
|
||||
|
||||
fn maybe_add_env_var(&mut self, key: &str, maybe_value: Option<impl ToString>) {
|
||||
if let Some(value) = maybe_value {
|
||||
self.0.insert(key.to_string(), value.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for EnvVar {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut result = String::new();
|
||||
for env_var in [
|
||||
"NODE_ENV",
|
||||
"RUST_LOG",
|
||||
"BIND",
|
||||
"PORT",
|
||||
"SOCKET",
|
||||
"SSE_FREQ",
|
||||
"WS_FREQ",
|
||||
"DATABASE_URL",
|
||||
"DB_USER",
|
||||
"USER",
|
||||
"DB_PORT",
|
||||
"DB_HOST",
|
||||
"DB_PASS",
|
||||
"DB_NAME",
|
||||
"DB_SSLMODE",
|
||||
"REDIS_HOST",
|
||||
"REDIS_USER",
|
||||
"REDIS_PORT",
|
||||
"REDIS_PASSWORD",
|
||||
"REDIS_USER",
|
||||
"REDIS_DB",
|
||||
]
|
||||
.iter()
|
||||
{
|
||||
if let Some(value) = self.get(&env_var.to_string()) {
|
||||
result = format!("{}\n {}: {}", result, env_var, value)
|
||||
}
|
||||
}
|
||||
write!(f, "{}", result)
|
||||
}
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! maybe_update {
|
||||
($name:ident; $item: tt:$type:ty) => (
|
||||
|
|
|
@ -1,90 +1,71 @@
|
|||
use super::EnvVar;
|
||||
use crate::{err, maybe_update};
|
||||
use super::{postgres_cfg_types::*, EnvVar};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PostgresConfig {
|
||||
pub user: String,
|
||||
pub host: String,
|
||||
pub password: Option<String>,
|
||||
pub database: String,
|
||||
pub port: u16,
|
||||
pub ssl_mode: String,
|
||||
pub user: PgUser,
|
||||
pub host: PgHost,
|
||||
pub password: PgPass,
|
||||
pub database: PgDatabase,
|
||||
pub port: PgPort,
|
||||
pub ssl_mode: PgSslMode,
|
||||
}
|
||||
|
||||
impl Default for PostgresConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
user: "postgres".to_string(),
|
||||
host: "localhost".to_string(),
|
||||
password: None,
|
||||
database: "mastodon_development".to_string(),
|
||||
port: 5432,
|
||||
ssl_mode: "prefer".to_string(),
|
||||
impl EnvVar {
|
||||
fn update_with_postgres_url(mut self, url_str: &str) -> Self {
|
||||
let url = Url::parse(url_str).unwrap();
|
||||
let none_if_empty = |s: String| if s.is_empty() { None } else { Some(s) };
|
||||
|
||||
for (k, v) in url.query_pairs().into_owned() {
|
||||
match k.to_string().as_str() {
|
||||
"user" => self.maybe_add_env_var("DB_USER", Some(v.to_string())),
|
||||
"password" => self.maybe_add_env_var("DB_PASS", Some(v.to_string())),
|
||||
"host" => self.maybe_add_env_var("DB_HOST", Some(v.to_string())),
|
||||
"sslmode" => self.maybe_add_env_var("DB_SSLMODE", Some(v.to_string())),
|
||||
_ => crate::err::die_with_msg(format!(
|
||||
r"Unsupported parameter {} in POSTGRES_URL
|
||||
Flodgatt supports only `password`, `user`, `host`, and `sslmode` parameters",
|
||||
k
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
self.maybe_add_env_var("DB_PORT", url.port());
|
||||
self.maybe_add_env_var("DB_PASS", url.password());
|
||||
self.maybe_add_env_var("DB_USER", none_if_empty(url.username().to_string()));
|
||||
self.maybe_add_env_var("DB_NAME", none_if_empty(url.path()[1..].to_string()));
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
fn none_if_empty(item: &str) -> Option<String> {
|
||||
Some(item).filter(|i| !i.is_empty()).map(String::from)
|
||||
}
|
||||
|
||||
impl PostgresConfig {
|
||||
/// Configure Postgres and return a connection
|
||||
pub fn from_env(env_vars: EnvVar) -> Self {
|
||||
// use openssl::ssl::{SslConnector, SslMethod};
|
||||
// use postgres_openssl::MakeTlsConnector;
|
||||
// let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||
// builder.set_ca_file("/etc/ssl/cert.pem").unwrap();
|
||||
// let connector = MakeTlsConnector::new(builder.build());
|
||||
// TODO: add TLS support, remove `NoTls`
|
||||
match env_vars.get("DATABASE_URL") {
|
||||
Some(url) => {
|
||||
log::warn!("DATABASE_URL env variable set. Connecting to Postgres with that URL and ignoring any values set in DB_HOST, DB_USER, DB_NAME, DB_PASS, or DB_PORT.");
|
||||
PostgresConfig::from_url(Url::parse(url).unwrap())
|
||||
}
|
||||
None => Self::default()
|
||||
.maybe_update_user(env_vars.get("USER").map(String::from))
|
||||
.maybe_update_user(env_vars.get("DB_USER").map(String::from))
|
||||
.maybe_update_host(env_vars.get("DB_HOST").map(String::from))
|
||||
.maybe_update_password(env_vars.get("DB_PASS").map(String::from))
|
||||
.maybe_update_db(env_vars.get("DB_NAME").map(String::from))
|
||||
.maybe_update_sslmode(env_vars.get("DB_SSLMODE").map(String::from))}
|
||||
.log()
|
||||
}
|
||||
maybe_update!(maybe_update_user; user: String);
|
||||
maybe_update!(maybe_update_host; host: String);
|
||||
maybe_update!(maybe_update_db; database: String);
|
||||
maybe_update!(maybe_update_port; port: u16);
|
||||
maybe_update!(maybe_update_sslmode; ssl_mode: String);
|
||||
maybe_update!(maybe_update_password; Some(password: String));
|
||||
|
||||
fn from_url(url: Url) -> Self {
|
||||
let (mut user, mut host, mut sslmode, mut password) = (None, None, None, None);
|
||||
for (k, v) in url.query_pairs() {
|
||||
match k.to_string().as_str() {
|
||||
"user" => { user = Some(v.to_string());},
|
||||
"password" => { password = Some(v.to_string());},
|
||||
"host" => { host = Some(v.to_string());},
|
||||
"sslmode" => { sslmode = Some(v.to_string());},
|
||||
_ => { err::die_with_msg(format!("Unsupported parameter {} in DATABASE_URL.\n Flodgatt supports only `user`, `password`, `host`, and `sslmode` parameters.", k))}
|
||||
}
|
||||
}
|
||||
pub fn from_env(env: EnvVar) -> Self {
|
||||
let env = match env.get("DATABASE_URL").cloned() {
|
||||
Some(url_str) => env.update_with_postgres_url(&url_str),
|
||||
None => env,
|
||||
};
|
||||
|
||||
Self::default()
|
||||
// Values from query parameter
|
||||
.maybe_update_user(user)
|
||||
.maybe_update_password(password)
|
||||
.maybe_update_host(host)
|
||||
.maybe_update_sslmode(sslmode)
|
||||
// Values from URL (which override query values if both are present)
|
||||
.maybe_update_user(none_if_empty(url.username()))
|
||||
.maybe_update_host(url.host_str().filter(|h| !h.is_empty()).map(String::from))
|
||||
.maybe_update_password(url.password().map(String::from))
|
||||
.maybe_update_port(url.port())
|
||||
.maybe_update_db(none_if_empty(&url.path()[1..]))
|
||||
}
|
||||
fn log(self) -> Self {
|
||||
log::warn!("Postgres configuration:\n{:#?}", &self);
|
||||
self
|
||||
let cfg = Self {
|
||||
user: PgUser::default().maybe_update(env.get("DB_USER")),
|
||||
host: PgHost::default().maybe_update(env.get("DB_HOST")),
|
||||
password: PgPass::default().maybe_update(env.get("DB_PASS")),
|
||||
database: PgDatabase::default().maybe_update(env.get("DB_NAME")),
|
||||
port: PgPort::default().maybe_update(env.get("DB_PORT")),
|
||||
ssl_mode: PgSslMode::default().maybe_update(env.get("DB_SSLMODE")),
|
||||
};
|
||||
|
||||
log::warn!("Postgres configuration:\n{:#?}", &cfg);
|
||||
|
||||
cfg
|
||||
}
|
||||
|
||||
// // use openssl::ssl::{SslConnector, SslMethod};
|
||||
// // use postgres_openssl::MakeTlsConnector;
|
||||
// // let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||
// // builder.set_ca_file("/etc/ssl/cert.pem").unwrap();
|
||||
// // let connector = MakeTlsConnector::new(builder.build());
|
||||
// // TODO: add TLS support, remove `NoTls`
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
use crate::from_env_var;
|
||||
use std::str::FromStr;
|
||||
use strum_macros::{EnumString, EnumVariantNames};
|
||||
|
||||
from_env_var!(
|
||||
/// The user to use for Postgres
|
||||
let name = PgUser;
|
||||
let default: String = "postgres".to_string();
|
||||
let (env_var, allowed_values) = ("DB_USER", "any string".to_string());
|
||||
let from_str = |s| Some(s.to_string());
|
||||
);
|
||||
|
||||
from_env_var!(
|
||||
/// The host address where Postgres is running)
|
||||
let name = PgHost;
|
||||
let default: String = "localhost".to_string();
|
||||
let (env_var, allowed_values) = ("DB_HOST", "any string".to_string());
|
||||
let from_str = |s| Some(s.to_string());
|
||||
);
|
||||
|
||||
from_env_var!(
|
||||
/// The password to use with Postgress
|
||||
let name = PgPass;
|
||||
let default: Option<String> = None;
|
||||
let (env_var, allowed_values) = ("DB_PASS", "any string".to_string());
|
||||
let from_str = |s| Some(Some(s.to_string()));
|
||||
);
|
||||
|
||||
from_env_var!(
|
||||
/// The Postgres database to use
|
||||
let name = PgDatabase;
|
||||
let default: String = "mastodon_development".to_string();
|
||||
let (env_var, allowed_values) = ("DB_NAME", "any string".to_string());
|
||||
let from_str = |s| Some(s.to_string());
|
||||
);
|
||||
|
||||
from_env_var!(
|
||||
/// The port Postgres is running on
|
||||
let name = PgPort;
|
||||
let default: u16 = 5432;
|
||||
let (env_var, allowed_values) = ("DB_PORT", "a number between 0 and 65535".to_string());
|
||||
let from_str = |s| s.parse().ok();
|
||||
);
|
||||
|
||||
from_env_var!(
|
||||
let name = PgSslMode;
|
||||
let default: PgSslInner = PgSslInner::Prefer;
|
||||
let (env_var, allowed_values) = ("DB_SSLMODE", format!("one of: {:?}", PgSslInner::variants()));
|
||||
let from_str = |s| PgSslInner::from_str(s).ok();
|
||||
);
|
||||
|
||||
#[derive(EnumString, EnumVariantNames, Debug)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum PgSslInner {
|
||||
Prefer,
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
use super::redis_cfg_types::*;
|
||||
use crate::config::EnvVar;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct RedisConfig {
|
||||
|
@ -15,6 +16,30 @@ pub struct RedisConfig {
|
|||
pub polling_interval: RedisInterval,
|
||||
}
|
||||
|
||||
impl EnvVar {
|
||||
fn update_with_redis_url(mut self, url_str: &str) -> Self {
|
||||
let url = Url::parse(url_str).unwrap();
|
||||
let none_if_empty = |s: String| if s.is_empty() { None } else { Some(s) };
|
||||
|
||||
self.maybe_add_env_var("REDIS_PORT", url.port());
|
||||
self.maybe_add_env_var("REDIS_PASSWORD", url.password());
|
||||
self.maybe_add_env_var("REDIS_USERNAME", none_if_empty(url.username().to_string()));
|
||||
self.maybe_add_env_var("REDIS_DB", none_if_empty(url.path()[1..].to_string()));
|
||||
for (k, v) in url.query_pairs().into_owned() {
|
||||
match k.to_string().as_str() {
|
||||
"password" => self.maybe_add_env_var("REDIS_PASSWORD", Some(v.to_string())),
|
||||
"db" => self.maybe_add_env_var("REDIS_DB", Some(v.to_string())),
|
||||
_ => crate::err::die_with_msg(format!(
|
||||
r"Unsupported parameter {} in REDIS_URL.
|
||||
Flodgatt supports only `password` and `db` parameters.",
|
||||
k
|
||||
)),
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RedisConfig {
|
||||
const USER_SET_WARNING: &'static str =
|
||||
"Redis user specified, but Redis did not ask for a username. Ignoring it.";
|
||||
|
@ -24,7 +49,7 @@ For similar functionality, you may wish to set a REDIS_NAMESPACE";
|
|||
|
||||
pub fn from_env(env: EnvVar) -> Self {
|
||||
let env = match env.get("REDIS_URL").cloned() {
|
||||
Some(url_str) => env.update_with_url(&url_str),
|
||||
Some(url_str) => env.update_with_redis_url(&url_str),
|
||||
None => env,
|
||||
};
|
||||
|
||||
|
@ -44,7 +69,7 @@ For similar functionality, you may wish to set a REDIS_NAMESPACE";
|
|||
if cfg.user.is_some() {
|
||||
log::warn!("{}", Self::USER_SET_WARNING);
|
||||
}
|
||||
log::info!("Redis configuration:\n{:#?},", &cfg);
|
||||
log::warn!("Redis configuration:\n{:#?},", &cfg);
|
||||
cfg
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,13 @@ fn main() {
|
|||
Some(_) => err::die_with_msg("Unknown ENV variable specified.\n Valid options are: `production` or `development`."),
|
||||
}).ok();
|
||||
let env_vars_map: HashMap<_, _> = dotenv::vars().collect();
|
||||
let env_vars = config::EnvVar(env_vars_map);
|
||||
let env_vars = config::EnvVar::new(env_vars_map);
|
||||
pretty_env_logger::init();
|
||||
|
||||
warn!(
|
||||
"Flodgatt recognized the following environmental variables:{}",
|
||||
env_vars.clone()
|
||||
);
|
||||
let redis_cfg = config::RedisConfig::from_env(env_vars.clone());
|
||||
let cfg = config::DeploymentConfig::from_env(env_vars.clone());
|
||||
|
||||
|
|
|
@ -9,10 +9,10 @@ impl PostgresConn {
|
|||
pub fn new(pg_cfg: config::PostgresConfig) -> Self {
|
||||
let mut con = postgres::Client::configure();
|
||||
con.user(&pg_cfg.user)
|
||||
.host(&pg_cfg.host)
|
||||
.port(pg_cfg.port)
|
||||
.host(&*pg_cfg.host.to_string())
|
||||
.port(*pg_cfg.port)
|
||||
.dbname(&pg_cfg.database);
|
||||
if let Some(password) = &pg_cfg.password {
|
||||
if let Some(password) = &*pg_cfg.password {
|
||||
con.password(password);
|
||||
};
|
||||
Self(Arc::new(Mutex::new(
|
||||
|
|
Loading…
Reference in New Issue