Compare commits

...

20 Commits

Author SHA1 Message Date
Quentin Dufour 7ebed96b44 do not assume Garage boots in 2sec during tests 2023-03-13 12:14:19 +01:00
Alex Auvolat 40fa1242f0 update cargo.nix 2023-03-10 18:15:06 +01:00
Jonathan Davies 9ea154ae9c admin/cluster.rs: Added rust_version. 2023-03-10 14:46:54 +00:00
Jonathan Davies 4421378023 garage/admin.rs: Display Rust version in stats output. 2023-03-10 14:46:54 +00:00
Jonathan Davies 25f2a46fc3 rpc/system_metrics.rs: Added rustversion label to garage_build_info metric. 2023-03-10 14:46:44 +00:00
Alex 9e061d5a70 Merge pull request 'Update logo for stickers' (#521) from logo_autocollants into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/521
2023-03-08 13:14:46 +00:00
vincent db69267a56 MàJ logo pour autocollants 2023-03-07 21:34:55 +01:00
Alex 2dc80abbb1 Merge pull request 'block/repair.rs: Added a random element of 10 days to SCRUB_INTERVAL' (#516) from jpds/garage:scrub-randomize-window into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/516
2023-03-06 14:11:25 +00:00
Jonathan Davies 148b66b843 block/manager.rs: Display scrub-next-run. 2023-03-06 13:43:09 +00:00
Jonathan Davies 53d09eb00f block/repair.rs: Added function and time_next_run_scrub with a random element of
10 days to SCRUB_INTERVAL to help balance scrub load across cluster.
2023-03-06 13:43:04 +00:00
Alex 00dcfc97a5 Merge pull request 'web_server.rs: Log X-Forwarded-For IP' (#504) from jpds/garage:web_server-log-x-forwarded-for into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/504
2023-03-06 12:33:06 +00:00
Jonathan Davies 4e0fc3d6c9 web/web_server.rs: Handle X-Forwarded-For here too. 2023-03-06 11:43:54 +00:00
Jonathan Davies e4e5196066 api/generic_server.rs: Use new handle_forwarded_for_headers() function. 2023-03-06 11:43:35 +00:00
Alex 0d0906b066 Merge pull request 'Clearer error message when LMDB has oom error (fix #517)' (#519) from lmdb-oom-message into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/519
2023-03-06 10:49:04 +00:00
Alex Auvolat b8123fb6cd Clearer error message when LMDB has oom error (fix #517) 2023-03-06 11:38:49 +01:00
Alex 3d37be33a8 Merge pull request 'binary-packages.md: Added.' (#515) from jpds/garage:doc-binary-packages into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/515
2023-03-06 10:17:19 +00:00
Jonathan Davies ff70e09aa0 util/forwarded_headers.rs: Generalized handle_forwarded_for_headers()
here.
2023-03-03 19:17:40 +00:00
Jonathan Davies f056ad569d binary-packages.md: Added. 2023-03-03 18:52:49 +00:00
Alex a5f7a79250 Merge pull request 'Add documentation on community Ansible roles' (#513) from baptiste/garage:doc_ansible into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/513
2023-03-02 11:59:07 +00:00
Baptiste Jonglez 3b22da251d Add documentation on community Ansible roles 2023-03-01 09:24:13 +01:00
23 changed files with 449 additions and 38 deletions
Generated
+1
View File
@@ -1309,6 +1309,7 @@ dependencies = [
"opentelemetry", "opentelemetry",
"rand", "rand",
"rmp-serde", "rmp-serde",
"rustc_version",
"serde", "serde",
"serde_json", "serde_json",
"sha2 0.10.6", "sha2 0.10.6",
+4 -1
View File
@@ -32,7 +32,7 @@ args@{
ignoreLockHash, ignoreLockHash,
}: }:
let let
nixifiedLockHash = "9941a539c374cfd67691e633af6c6a5da9e8badbb7b14159b3ff24a7cf5ddf8a"; nixifiedLockHash = "0ee5ebe74e19a2d2d3c41b80ed443c93f079f696681902dda4ca659be282d985";
workspaceSrc = if args.workspaceSrc == null then ./. else args.workspaceSrc; workspaceSrc = if args.workspaceSrc == null then ./. else args.workspaceSrc;
currentLockHash = builtins.hashFile "sha256" (workspaceSrc + /Cargo.lock); currentLockHash = builtins.hashFile "sha256" (workspaceSrc + /Cargo.lock);
lockHashIgnored = if ignoreLockHash lockHashIgnored = if ignoreLockHash
@@ -1861,6 +1861,9 @@ in
devDependencies = { devDependencies = {
mktemp = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".mktemp."0.5.0" { inherit profileName; }).out; mktemp = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".mktemp."0.5.0" { inherit profileName; }).out;
}; };
buildDependencies = {
rustc_version = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".rustc_version."0.4.0" { profileName = "__noProfile"; }).out;
};
}); });
"unknown".garage_web."0.8.1" = overridableMkRustCrate (profileName: rec { "unknown".garage_web."0.8.1" = overridableMkRustCrate (profileName: rec {
+9 -1
View File
@@ -6,7 +6,7 @@ sort_by = "weight"
+++ +++
A cookbook, when you cook, is a collection of recipes. A cookbook, when you cook, is a collection of recipes.
Similarly, Garage's cookbook contains a collection of recipes that are known to works well! Similarly, Garage's cookbook contains a collection of recipes that are known to work well!
This chapter could also be referred as "Tutorials" or "Best practices". This chapter could also be referred as "Tutorials" or "Best practices".
- **[Multi-node deployment](@/documentation/cookbook/real-world.md):** This page will walk you through all of the necessary - **[Multi-node deployment](@/documentation/cookbook/real-world.md):** This page will walk you through all of the necessary
@@ -16,6 +16,10 @@ This chapter could also be referred as "Tutorials" or "Best practices".
source in case a binary is not provided for your architecture, or if you want to source in case a binary is not provided for your architecture, or if you want to
hack with us! hack with us!
- **[Binary packages](@/documentation/cookbook/binary-packages.md):** This page
lists the different platforms that provide ready-built software packages for
Garage.
- **[Integration with Systemd](@/documentation/cookbook/systemd.md):** This page explains how to run Garage - **[Integration with Systemd](@/documentation/cookbook/systemd.md):** This page explains how to run Garage
as a Systemd service (instead of as a Docker container). as a Systemd service (instead of as a Docker container).
@@ -26,6 +30,10 @@ This chapter could also be referred as "Tutorials" or "Best practices".
- **[Configuring a reverse-proxy](@/documentation/cookbook/reverse-proxy.md):** This page explains how to configure a reverse-proxy to add TLS support to your S3 api endpoint. - **[Configuring a reverse-proxy](@/documentation/cookbook/reverse-proxy.md):** This page explains how to configure a reverse-proxy to add TLS support to your S3 api endpoint.
- **[Deploying on Kubernetes](@/documentation/cookbook/kubernetes.md):** This page explains how to deploy Garage on Kubernetes using our Helm chart.
- **[Deploying with Ansible](@/documentation/cookbook/ansible.md):** This page lists available Ansible roles developed by the community to deploy Garage.
- **[Monitoring Garage](@/documentation/cookbook/monitoring.md)** This page - **[Monitoring Garage](@/documentation/cookbook/monitoring.md)** This page
explains the Prometheus metrics available for monitoring the Garage explains the Prometheus metrics available for monitoring the Garage
cluster/nodes. cluster/nodes.
+51
View File
@@ -0,0 +1,51 @@
+++
title = "Deploying with Ansible"
weight = 35
+++
While Ansible is not officially supported to deploy Garage, several community members
have published Ansible roles. We list them and compare them below.
## Comparison of Ansible roles
| Feature | [ansible-role-garage](#zorun-ansible-role-garage) | [garage-docker-ansible-deploy](#moan0s-garage-docker-ansible-deploy) |
|------------------------------------|---------------------------------------------|---------------------------------------------------------------|
| **Runtime** | Systemd | Docker |
| **Target OS** | Any Linux | Any Linux |
| **Architecture** | amd64, arm64, i686 | amd64, arm64 |
| **Additional software** | None | Traefik |
| **Automatic node connection** | ❌ | ✅ |
| **Layout management** | ❌ | ✅ |
| **Manage buckets & keys** | ❌ | ✅ (basic) |
| **Allow custom Garage config** | ✅ | ❌ |
| **Facilitate Garage upgrades** | ✅ | ❌ |
| **Multiple instances on one host** | ✅ | ✅ |
## zorun/ansible-role-garage
[Source code](https://github.com/zorun/ansible-role-garage), [Ansible galaxy](https://galaxy.ansible.com/zorun/garage)
This role is voluntarily simple: it relies on the official Garage static
binaries and only requires Systemd. As such, it should work on any
Linux-based OS.
To make things more flexible, the user has to provide a Garage
configuration template. This allows to customize Garage configuration in
any way.
Some more features might be added, such as a way to automatically connect
nodes to each other or to define a layout.
## moan0s/garage-docker-ansible-deploy
[Source code](https://github.com/moan0s/garage-docker-ansible-deploy), [Blog post](https://hyteck.de/post/garage/)
This role is based on the Docker image for Garage, and comes with
"batteries included": it will additionally install Docker and Traefik. In
addition, it is "opinionated" in the sense that it expects a particular
deployment structure (one instance per disk, one gateway per host,
structured DNS names, etc).
As a result, this role makes it easier to start with Garage on Ansible,
but is less flexible.
+28
View File
@@ -0,0 +1,28 @@
+++
title = "Binary packages"
weight = 11
+++
Garage is also available in binary packages on:
## Alpine Linux
```bash
apk install garage
```
## Arch Linux
Garage is available in the [AUR](https://aur.archlinux.org/packages/garage).
## FreeBSD
```bash
pkg install garage
```
## NixOS
```bash
nix-shell -p garage
```
+181 -1
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 74 KiB

+2
View File
@@ -20,6 +20,7 @@ pub async fn handle_get_cluster_status(garage: &Arc<Garage>) -> Result<Response<
node: hex::encode(garage.system.id), node: hex::encode(garage.system.id),
garage_version: garage_util::version::garage_version(), garage_version: garage_util::version::garage_version(),
garage_features: garage_util::version::garage_features(), garage_features: garage_util::version::garage_features(),
rust_version: garage_util::version::rust_version(),
db_engine: garage.db.engine(), db_engine: garage.db.engine(),
known_nodes: garage known_nodes: garage
.system .system
@@ -106,6 +107,7 @@ struct GetClusterStatusResponse {
node: String, node: String,
garage_version: &'static str, garage_version: &'static str,
garage_features: Option<&'static [&'static str]>, garage_features: Option<&'static [&'static str]>,
rust_version: &'static str,
db_engine: String, db_engine: String,
known_nodes: HashMap<String, KnownNodeResp>, known_nodes: HashMap<String, KnownNodeResp>,
layout: GetClusterLayoutResponse, layout: GetClusterLayoutResponse,
+4 -9
View File
@@ -19,6 +19,7 @@ use opentelemetry::{
}; };
use garage_util::error::Error as GarageError; use garage_util::error::Error as GarageError;
use garage_util::forwarded_headers;
use garage_util::metrics::{gen_trace_id, RecordDuration}; use garage_util::metrics::{gen_trace_id, RecordDuration};
pub(crate) trait ApiEndpoint: Send + Sync + 'static { pub(crate) trait ApiEndpoint: Send + Sync + 'static {
@@ -126,15 +127,9 @@ impl<A: ApiHandler> ApiServer<A> {
) -> Result<Response<Body>, GarageError> { ) -> Result<Response<Body>, GarageError> {
let uri = req.uri().clone(); let uri = req.uri().clone();
let has_forwarded_for_header = req.headers().contains_key("x-forwarded-for"); if let Ok(forwarded_for_ip_addr) =
if has_forwarded_for_header { forwarded_headers::handle_forwarded_for_headers(&req.headers())
let forwarded_for_ip_addr = &req {
.headers()
.get("x-forwarded-for")
.expect("Could not parse X-Forwarded-For header")
.to_str()
.unwrap_or_default();
info!( info!(
"{} (via {}) {} {}", "{} (via {}) {} {}",
forwarded_for_ip_addr, forwarded_for_ip_addr,
+3
View File
@@ -185,6 +185,9 @@ impl BlockManager {
vars.register_ro(&self.scrub_persister, "scrub-last-completed", |p| { vars.register_ro(&self.scrub_persister, "scrub-last-completed", |p| {
p.get_with(|x| msec_to_rfc3339(x.time_last_complete_scrub)) p.get_with(|x| msec_to_rfc3339(x.time_last_complete_scrub))
}); });
vars.register_ro(&self.scrub_persister, "scrub-next-run", |p| {
p.get_with(|x| msec_to_rfc3339(x.time_next_run_scrub))
});
vars.register_ro(&self.scrub_persister, "scrub-corruptions_detected", |p| { vars.register_ro(&self.scrub_persister, "scrub-corruptions_detected", |p| {
p.get_with(|x| x.corruptions_detected) p.get_with(|x| x.corruptions_detected)
}); });
+37 -11
View File
@@ -4,6 +4,7 @@ use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use async_trait::async_trait; use async_trait::async_trait;
use rand::Rng;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::fs; use tokio::fs;
use tokio::select; use tokio::select;
@@ -19,8 +20,8 @@ use garage_util::tranquilizer::Tranquilizer;
use crate::manager::*; use crate::manager::*;
// Full scrub every 30 days // Full scrub every 25 days with a random element of 10 days mixed in below
const SCRUB_INTERVAL: Duration = Duration::from_secs(3600 * 24 * 30); const SCRUB_INTERVAL: Duration = Duration::from_secs(3600 * 24 * 25);
// Scrub tranquility is initially set to 4, but can be changed in the CLI // Scrub tranquility is initially set to 4, but can be changed in the CLI
// and the updated version is persisted over Garage restarts // and the updated version is persisted over Garage restarts
const INITIAL_SCRUB_TRANQUILITY: u32 = 4; const INITIAL_SCRUB_TRANQUILITY: u32 = 4;
@@ -175,13 +176,30 @@ pub struct ScrubWorker {
pub struct ScrubWorkerPersisted { pub struct ScrubWorkerPersisted {
pub tranquility: u32, pub tranquility: u32,
pub(crate) time_last_complete_scrub: u64, pub(crate) time_last_complete_scrub: u64,
pub(crate) time_next_run_scrub: u64,
pub(crate) corruptions_detected: u64, pub(crate) corruptions_detected: u64,
} }
fn randomize_next_scrub_run_time() -> u64 {
// Take SCRUB_INTERVAL and mix in a random interval of 10 days to attempt to
// balance scrub load across different cluster nodes.
let next_run_timestamp = now_msec()
+ SCRUB_INTERVAL
.saturating_add(Duration::from_secs(
rand::thread_rng().gen_range(0..3600 * 24 * 10),
))
.as_millis() as u64;
next_run_timestamp
}
impl garage_util::migrate::InitialFormat for ScrubWorkerPersisted {} impl garage_util::migrate::InitialFormat for ScrubWorkerPersisted {}
impl Default for ScrubWorkerPersisted { impl Default for ScrubWorkerPersisted {
fn default() -> Self { fn default() -> Self {
ScrubWorkerPersisted { ScrubWorkerPersisted {
time_last_complete_scrub: 0, time_last_complete_scrub: 0,
time_next_run_scrub: randomize_next_scrub_run_time(),
tranquility: INITIAL_SCRUB_TRANQUILITY, tranquility: INITIAL_SCRUB_TRANQUILITY,
corruptions_detected: 0, corruptions_detected: 0,
} }
@@ -279,12 +297,13 @@ impl Worker for ScrubWorker {
} }
fn status(&self) -> WorkerStatus { fn status(&self) -> WorkerStatus {
let (corruptions_detected, tranquility, time_last_complete_scrub) = let (corruptions_detected, tranquility, time_last_complete_scrub, time_next_run_scrub) =
self.persister.get_with(|p| { self.persister.get_with(|p| {
( (
p.corruptions_detected, p.corruptions_detected,
p.tranquility, p.tranquility,
p.time_last_complete_scrub, p.time_last_complete_scrub,
p.time_next_run_scrub,
) )
}); });
@@ -302,10 +321,16 @@ impl Worker for ScrubWorker {
s.freeform = vec![format!("Scrub paused, resumes at {}", msec_to_rfc3339(*rt))]; s.freeform = vec![format!("Scrub paused, resumes at {}", msec_to_rfc3339(*rt))];
} }
ScrubWorkerState::Finished => { ScrubWorkerState::Finished => {
s.freeform = vec![format!( s.freeform = vec![
"Last scrub completed at {}", format!(
msec_to_rfc3339(time_last_complete_scrub) "Last scrub completed at {}",
)]; msec_to_rfc3339(time_last_complete_scrub),
),
format!(
"Next scrub scheduled for {}",
msec_to_rfc3339(time_next_run_scrub)
),
];
} }
} }
s s
@@ -334,8 +359,10 @@ impl Worker for ScrubWorker {
.tranquilizer .tranquilizer
.tranquilize_worker(self.persister.get_with(|p| p.tranquility))) .tranquilize_worker(self.persister.get_with(|p| p.tranquility)))
} else { } else {
self.persister self.persister.set_with(|p| {
.set_with(|p| p.time_last_complete_scrub = now_msec())?; p.time_last_complete_scrub = now_msec();
p.time_next_run_scrub = randomize_next_scrub_run_time();
})?;
self.work = ScrubWorkerState::Finished; self.work = ScrubWorkerState::Finished;
self.tranquilizer.clear(); self.tranquilizer.clear();
Ok(WorkerState::Idle) Ok(WorkerState::Idle)
@@ -350,8 +377,7 @@ impl Worker for ScrubWorker {
ScrubWorkerState::Running(_) => return WorkerState::Busy, ScrubWorkerState::Running(_) => return WorkerState::Busy,
ScrubWorkerState::Paused(_, resume_time) => (*resume_time, ScrubWorkerCommand::Resume), ScrubWorkerState::Paused(_, resume_time) => (*resume_time, ScrubWorkerCommand::Resume),
ScrubWorkerState::Finished => ( ScrubWorkerState::Finished => (
self.persister.get_with(|p| p.time_last_complete_scrub) self.persister.get_with(|p| p.time_next_run_scrub),
+ SCRUB_INTERVAL.as_millis() as u64,
ScrubWorkerCommand::Start, ScrubWorkerCommand::Start,
), ),
}; };
+2 -1
View File
@@ -820,11 +820,12 @@ impl AdminRpcHandler {
let mut ret = String::new(); let mut ret = String::new();
writeln!( writeln!(
&mut ret, &mut ret,
"\nGarage version: {} [features: {}]", "\nGarage version: {} [features: {}]\nRust compiler version: {}",
garage_util::version::garage_version(), garage_util::version::garage_version(),
garage_util::version::garage_features() garage_util::version::garage_features()
.map(|list| list.join(", ")) .map(|list| list.join(", "))
.unwrap_or_else(|| "(unknown)".into()), .unwrap_or_else(|| "(unknown)".into()),
garage_util::version::rust_version(),
) )
.unwrap(); .unwrap();
+19 -6
View File
@@ -111,14 +111,27 @@ api_bind_addr = "127.0.0.1:{admin_port}"
} }
fn setup(&mut self) { fn setup(&mut self) {
self.wait_for_boot();
self.setup_layout();
self.key = self.new_key("garage_test");
}
fn wait_for_boot(&mut self) {
use std::{thread, time::Duration}; use std::{thread, time::Duration};
// Wait for node to be ready // 60 * 2 seconds = 120 seconds = 2min
thread::sleep(Duration::from_secs(2)); for _ in 0..60 {
let termination = self
self.setup_layout(); .command()
.args(["status"])
self.key = self.new_key("garage_test"); .quiet()
.status()
.expect("Unable to run command");
if termination.success() {
break;
}
thread::sleep(Duration::from_secs(2));
}
} }
fn setup_layout(&self) { fn setup_layout(&self) {
+10 -3
View File
@@ -136,9 +136,16 @@ impl Garage {
env_builder.flag(heed::flags::Flags::MdbNoSync); env_builder.flag(heed::flags::Flags::MdbNoSync);
env_builder.flag(heed::flags::Flags::MdbNoMetaSync); env_builder.flag(heed::flags::Flags::MdbNoMetaSync);
} }
let db = env_builder let db = match env_builder.open(&db_path) {
.open(&db_path) Err(heed::Error::Io(e)) if e.kind() == std::io::ErrorKind::OutOfMemory => {
.ok_or_message("Unable to open LMDB DB")?; return Err(Error::Message(
"OutOfMemory error while trying to open LMDB database. This can happen \
if your operating system is not allowing you to use sufficient virtual \
memory address space. Please check that no limit is set (ulimit -v). \
On 32-bit machines, you should probably switch to another database engine.".into()))
}
x => x.ok_or_message("Unable to open LMDB DB")?,
};
db::lmdb_adapter::LmdbDb::init(db) db::lmdb_adapter::LmdbDb::init(db)
} }
#[cfg(not(feature = "lmdb"))] #[cfg(not(feature = "lmdb"))]
+4 -4
View File
@@ -31,10 +31,10 @@ impl SystemMetrics {
.u64_value_observer("garage_build_info", move |observer| { .u64_value_observer("garage_build_info", move |observer| {
observer.observe( observer.observe(
1, 1,
&[KeyValue::new( &[
"version", KeyValue::new("rustversion", garage_util::version::rust_version()),
garage_util::version::garage_version(), KeyValue::new("version", garage_util::version::garage_version()),
)], ],
) )
}) })
.with_description("Garage build info") .with_description("Garage build info")
+3
View File
@@ -47,6 +47,9 @@ hyper = "0.14"
opentelemetry = { version = "0.17", features = [ "rt-tokio", "metrics", "trace" ] } opentelemetry = { version = "0.17", features = [ "rt-tokio", "metrics", "trace" ] }
[build-dependencies]
rustc_version = "0.4.0"
[dev-dependencies] [dev-dependencies]
mktemp = "0.5" mktemp = "0.5"
+8
View File
@@ -0,0 +1,8 @@
use rustc_version::version;
fn main() {
// Acquire the version of Rust used to compile, this is added as a label to
// the garage_build_info metric.
let v = version().unwrap();
println!("cargo:rustc-env=RUSTC_VERSION={v}");
}
+63
View File
@@ -0,0 +1,63 @@
use http::{HeaderMap, HeaderValue};
use std::net::IpAddr;
use std::str::FromStr;
use crate::error::{Error, OkOrMessage};
pub fn handle_forwarded_for_headers(headers: &HeaderMap<HeaderValue>) -> Result<String, Error> {
let forwarded_for_header = headers
.get("x-forwarded-for")
.ok_or_message("X-Forwarded-For header not provided")?;
let forwarded_for_ip_str = forwarded_for_header
.to_str()
.ok_or_message("Error parsing X-Forwarded-For header")?;
let client_ip = IpAddr::from_str(&forwarded_for_ip_str)
.ok_or_message("Valid IP address not found in X-Forwarded-For header")?;
Ok(client_ip.to_string())
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_handle_forwarded_for_headers_ipv4_client() {
let mut test_headers = HeaderMap::new();
test_headers.insert("X-Forwarded-For", "192.0.2.100".parse().unwrap());
if let Ok(forwarded_ip) = handle_forwarded_for_headers(&test_headers) {
assert_eq!(forwarded_ip, "192.0.2.100");
}
}
#[test]
fn test_handle_forwarded_for_headers_ipv6_client() {
let mut test_headers = HeaderMap::new();
test_headers.insert("X-Forwarded-For", "2001:db8::f00d:cafe".parse().unwrap());
if let Ok(forwarded_ip) = handle_forwarded_for_headers(&test_headers) {
assert_eq!(forwarded_ip, "2001:db8::f00d:cafe");
}
}
#[test]
fn test_handle_forwarded_for_headers_invalid_ip() {
let mut test_headers = HeaderMap::new();
test_headers.insert("X-Forwarded-For", "www.example.com".parse().unwrap());
let result = handle_forwarded_for_headers(&test_headers);
assert!(result.is_err());
}
#[test]
fn test_handle_forwarded_for_headers_missing() {
let mut test_headers = HeaderMap::new();
test_headers.insert("Host", "www.deuxfleurs.fr".parse().unwrap());
let result = handle_forwarded_for_headers(&test_headers);
assert!(result.is_err());
}
}
+1
View File
@@ -11,6 +11,7 @@ pub mod data;
pub mod encode; pub mod encode;
pub mod error; pub mod error;
pub mod formater; pub mod formater;
pub mod forwarded_headers;
pub mod metrics; pub mod metrics;
pub mod migrate; pub mod migrate;
pub mod persister; pub mod persister;
+4
View File
@@ -26,3 +26,7 @@ pub fn init_version(version: &'static str) {
pub fn init_features(features: &'static [&'static str]) { pub fn init_features(features: &'static [&'static str]) {
FEATURES.store(Some(Arc::new(features))); FEATURES.store(Some(Arc::new(features)));
} }
pub fn rust_version() -> &'static str {
env!("RUSTC_VERSION")
}
+14 -1
View File
@@ -29,6 +29,7 @@ use garage_model::garage::Garage;
use garage_table::*; use garage_table::*;
use garage_util::error::Error as GarageError; use garage_util::error::Error as GarageError;
use garage_util::forwarded_headers;
use garage_util::metrics::{gen_trace_id, RecordDuration}; use garage_util::metrics::{gen_trace_id, RecordDuration};
struct WebMetrics { struct WebMetrics {
@@ -104,7 +105,19 @@ impl WebServer {
req: Request<Body>, req: Request<Body>,
addr: SocketAddr, addr: SocketAddr,
) -> Result<Response<Body>, Infallible> { ) -> Result<Response<Body>, Infallible> {
info!("{} {} {}", addr, req.method(), req.uri()); if let Ok(forwarded_for_ip_addr) =
forwarded_headers::handle_forwarded_for_headers(&req.headers())
{
info!(
"{} (via {}) {} {}",
forwarded_for_ip_addr,
addr,
req.method(),
req.uri()
);
} else {
info!("{} {} {}", addr, req.method(), req.uri());
}
// Lots of instrumentation // Lots of instrumentation
let tracer = opentelemetry::global::tracer("garage"); let tracer = opentelemetry::global::tracer("garage");