mirror of
https://github.com/deuxfleurs-org/garage.git
synced 2026-08-15 00:23:11 +00:00
Compare commits
27 Commits
v0.9.0-beta4
...
v0.8.5
| Author | SHA1 | Date | |
|---|---|---|---|
| 083e982f5f | |||
| 50643e61bf | |||
| a6421ee5a5 | |||
| 993ce74976 | |||
| f512609123 | |||
| 97bae7213a | |||
| 7228695ee2 | |||
| ee7fe27d3d | |||
| f83fa02193 | |||
| 4b3dee2ca3 | |||
| ac04934dae | |||
| 0215b11402 | |||
| 8599051c49 | |||
| 4a19ee94bb | |||
| c99cb58d71 | |||
| 5feb6a1f64 | |||
| e75fe2157d | |||
| 2d5d7a7031 | |||
| 1243db87f2 | |||
| 6f8a87814b | |||
| 7907a09acc | |||
| 16aa418e47 | |||
| cb359b4434 | |||
| 8ec6a53b35 | |||
| 7353038a64 | |||
| 10195f1567 | |||
| 6086a3fa07 |
Generated
+26
-9
@@ -1198,7 +1198,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "garage"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
dependencies = [
|
||||
"assert-json-diff",
|
||||
"async-trait",
|
||||
@@ -1227,6 +1227,7 @@ dependencies = [
|
||||
"hyper",
|
||||
"k2v-client",
|
||||
"kuska-sodiumoxide",
|
||||
"mktemp",
|
||||
"netapp",
|
||||
"opentelemetry",
|
||||
"opentelemetry-otlp",
|
||||
@@ -1249,7 +1250,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "garage_api"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.21.3",
|
||||
@@ -1271,6 +1272,7 @@ dependencies = [
|
||||
"http-range",
|
||||
"httpdate",
|
||||
"hyper",
|
||||
"hyperlocal",
|
||||
"idna",
|
||||
"md-5",
|
||||
"multer",
|
||||
@@ -1294,7 +1296,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "garage_block"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-compression",
|
||||
@@ -1319,7 +1321,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "garage_db"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
dependencies = [
|
||||
"clap 4.4.0",
|
||||
"err-derive",
|
||||
@@ -1334,7 +1336,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "garage_model"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
@@ -1361,7 +1363,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "garage_rpc"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
@@ -1393,7 +1395,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "garage_table"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
@@ -1415,7 +1417,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "garage_util"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
@@ -1449,7 +1451,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "garage_web"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
dependencies = [
|
||||
"err-derive",
|
||||
"futures",
|
||||
@@ -1459,8 +1461,10 @@ dependencies = [
|
||||
"garage_util",
|
||||
"http",
|
||||
"hyper",
|
||||
"hyperlocal",
|
||||
"opentelemetry",
|
||||
"percent-encoding",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
@@ -1770,6 +1774,19 @@ dependencies = [
|
||||
"tokio-io-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyperlocal"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"hex",
|
||||
"hyper",
|
||||
"pin-project",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.57"
|
||||
|
||||
@@ -33,7 +33,7 @@ args@{
|
||||
ignoreLockHash,
|
||||
}:
|
||||
let
|
||||
nixifiedLockHash = "ab8dba64b487c83fa25ebafbad99538d2f38afa5b53f83e4e031b7909665166b";
|
||||
nixifiedLockHash = "79d4a9e02f28a0c0f31576781738e132ac96ff4944b3bc2ca7375c6dd15b83ce";
|
||||
workspaceSrc = if args.workspaceSrc == null then ./. else args.workspaceSrc;
|
||||
currentLockHash = builtins.hashFile "sha256" (workspaceSrc + /Cargo.lock);
|
||||
lockHashIgnored = if ignoreLockHash
|
||||
@@ -57,15 +57,15 @@ in
|
||||
{
|
||||
cargo2nixVersion = "0.11.0";
|
||||
workspace = {
|
||||
garage_db = rustPackages.unknown.garage_db."0.8.4";
|
||||
garage_util = rustPackages.unknown.garage_util."0.8.4";
|
||||
garage_rpc = rustPackages.unknown.garage_rpc."0.8.4";
|
||||
garage_table = rustPackages.unknown.garage_table."0.8.4";
|
||||
garage_block = rustPackages.unknown.garage_block."0.8.4";
|
||||
garage_model = rustPackages.unknown.garage_model."0.8.4";
|
||||
garage_api = rustPackages.unknown.garage_api."0.8.4";
|
||||
garage_web = rustPackages.unknown.garage_web."0.8.4";
|
||||
garage = rustPackages.unknown.garage."0.8.4";
|
||||
garage_db = rustPackages.unknown.garage_db."0.8.5";
|
||||
garage_util = rustPackages.unknown.garage_util."0.8.5";
|
||||
garage_rpc = rustPackages.unknown.garage_rpc."0.8.5";
|
||||
garage_table = rustPackages.unknown.garage_table."0.8.5";
|
||||
garage_block = rustPackages.unknown.garage_block."0.8.5";
|
||||
garage_model = rustPackages.unknown.garage_model."0.8.5";
|
||||
garage_api = rustPackages.unknown.garage_api."0.8.5";
|
||||
garage_web = rustPackages.unknown.garage_web."0.8.5";
|
||||
garage = rustPackages.unknown.garage."0.8.5";
|
||||
format_table = rustPackages.unknown.format_table."0.1.1";
|
||||
k2v-client = rustPackages.unknown.k2v-client."0.0.4";
|
||||
};
|
||||
@@ -1705,9 +1705,9 @@ in
|
||||
};
|
||||
});
|
||||
|
||||
"unknown".garage."0.8.4" = overridableMkRustCrate (profileName: rec {
|
||||
"unknown".garage."0.8.5" = overridableMkRustCrate (profileName: rec {
|
||||
name = "garage";
|
||||
version = "0.8.4";
|
||||
version = "0.8.5";
|
||||
registry = "unknown";
|
||||
src = fetchCrateLocal (workspaceSrc + "/src/garage");
|
||||
features = builtins.concatLists [
|
||||
@@ -1734,14 +1734,14 @@ in
|
||||
format_table = (rustPackages."unknown".format_table."0.1.1" { inherit profileName; }).out;
|
||||
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.28" { inherit profileName; }).out;
|
||||
futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.28" { inherit profileName; }).out;
|
||||
garage_api = (rustPackages."unknown".garage_api."0.8.4" { inherit profileName; }).out;
|
||||
garage_block = (rustPackages."unknown".garage_block."0.8.4" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."0.8.4" { inherit profileName; }).out;
|
||||
garage_model = (rustPackages."unknown".garage_model."0.8.4" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."0.8.4" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."0.8.4" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."0.8.4" { inherit profileName; }).out;
|
||||
garage_web = (rustPackages."unknown".garage_web."0.8.4" { inherit profileName; }).out;
|
||||
garage_api = (rustPackages."unknown".garage_api."0.8.5" { inherit profileName; }).out;
|
||||
garage_block = (rustPackages."unknown".garage_block."0.8.5" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."0.8.5" { inherit profileName; }).out;
|
||||
garage_model = (rustPackages."unknown".garage_model."0.8.5" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."0.8.5" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."0.8.5" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."0.8.5" { inherit profileName; }).out;
|
||||
garage_web = (rustPackages."unknown".garage_web."0.8.5" { inherit profileName; }).out;
|
||||
git_version = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".git-version."0.3.5" { inherit profileName; }).out;
|
||||
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
|
||||
sodiumoxide = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".kuska-sodiumoxide."0.2.5-0" { inherit profileName; }).out;
|
||||
@@ -1771,15 +1771,16 @@ in
|
||||
http = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".http."0.2.9" { inherit profileName; }).out;
|
||||
hyper = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hyper."0.14.27" { inherit profileName; }).out;
|
||||
k2v_client = (rustPackages."unknown".k2v-client."0.0.4" { inherit profileName; }).out;
|
||||
mktemp = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".mktemp."0.5.0" { inherit profileName; }).out;
|
||||
serde_json = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".serde_json."1.0.105" { inherit profileName; }).out;
|
||||
sha2 = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".sha2."0.10.7" { inherit profileName; }).out;
|
||||
static_init = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".static_init."1.0.3" { inherit profileName; }).out;
|
||||
};
|
||||
});
|
||||
|
||||
"unknown".garage_api."0.8.4" = overridableMkRustCrate (profileName: rec {
|
||||
"unknown".garage_api."0.8.5" = overridableMkRustCrate (profileName: rec {
|
||||
name = "garage_api";
|
||||
version = "0.8.4";
|
||||
version = "0.8.5";
|
||||
registry = "unknown";
|
||||
src = fetchCrateLocal (workspaceSrc + "/src/api");
|
||||
features = builtins.concatLists [
|
||||
@@ -1798,17 +1799,18 @@ in
|
||||
form_urlencoded = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".form_urlencoded."1.2.0" { inherit profileName; }).out;
|
||||
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.28" { inherit profileName; }).out;
|
||||
futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.28" { inherit profileName; }).out;
|
||||
garage_block = (rustPackages."unknown".garage_block."0.8.4" { inherit profileName; }).out;
|
||||
garage_model = (rustPackages."unknown".garage_model."0.8.4" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."0.8.4" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."0.8.4" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."0.8.4" { inherit profileName; }).out;
|
||||
garage_block = (rustPackages."unknown".garage_block."0.8.5" { inherit profileName; }).out;
|
||||
garage_model = (rustPackages."unknown".garage_model."0.8.5" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."0.8.5" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."0.8.5" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."0.8.5" { inherit profileName; }).out;
|
||||
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
|
||||
hmac = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hmac."0.12.1" { inherit profileName; }).out;
|
||||
http = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".http."0.2.9" { inherit profileName; }).out;
|
||||
http_range = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".http-range."0.1.5" { inherit profileName; }).out;
|
||||
httpdate = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".httpdate."1.0.3" { inherit profileName; }).out;
|
||||
hyper = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hyper."0.14.27" { inherit profileName; }).out;
|
||||
hyperlocal = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hyperlocal."0.8.0" { inherit profileName; }).out;
|
||||
idna = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".idna."0.4.0" { inherit profileName; }).out;
|
||||
md5 = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".md-5."0.10.5" { inherit profileName; }).out;
|
||||
multer = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".multer."2.1.0" { inherit profileName; }).out;
|
||||
@@ -1831,9 +1833,9 @@ in
|
||||
};
|
||||
});
|
||||
|
||||
"unknown".garage_block."0.8.4" = overridableMkRustCrate (profileName: rec {
|
||||
"unknown".garage_block."0.8.5" = overridableMkRustCrate (profileName: rec {
|
||||
name = "garage_block";
|
||||
version = "0.8.4";
|
||||
version = "0.8.5";
|
||||
registry = "unknown";
|
||||
src = fetchCrateLocal (workspaceSrc + "/src/block");
|
||||
features = builtins.concatLists [
|
||||
@@ -1846,10 +1848,10 @@ in
|
||||
bytes = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".bytes."1.4.0" { inherit profileName; }).out;
|
||||
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.28" { inherit profileName; }).out;
|
||||
futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.28" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."0.8.4" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."0.8.4" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."0.8.4" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."0.8.4" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."0.8.5" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."0.8.5" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."0.8.5" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."0.8.5" { inherit profileName; }).out;
|
||||
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
|
||||
opentelemetry = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".opentelemetry."0.17.0" { inherit profileName; }).out;
|
||||
rand = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".rand."0.8.5" { inherit profileName; }).out;
|
||||
@@ -1862,9 +1864,9 @@ in
|
||||
};
|
||||
});
|
||||
|
||||
"unknown".garage_db."0.8.4" = overridableMkRustCrate (profileName: rec {
|
||||
"unknown".garage_db."0.8.5" = overridableMkRustCrate (profileName: rec {
|
||||
name = "garage_db";
|
||||
version = "0.8.4";
|
||||
version = "0.8.5";
|
||||
registry = "unknown";
|
||||
src = fetchCrateLocal (workspaceSrc + "/src/db");
|
||||
features = builtins.concatLists [
|
||||
@@ -1894,9 +1896,9 @@ in
|
||||
};
|
||||
});
|
||||
|
||||
"unknown".garage_model."0.8.4" = overridableMkRustCrate (profileName: rec {
|
||||
"unknown".garage_model."0.8.5" = overridableMkRustCrate (profileName: rec {
|
||||
name = "garage_model";
|
||||
version = "0.8.4";
|
||||
version = "0.8.5";
|
||||
registry = "unknown";
|
||||
src = fetchCrateLocal (workspaceSrc + "/src/model");
|
||||
features = builtins.concatLists [
|
||||
@@ -1914,11 +1916,11 @@ in
|
||||
err_derive = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".err-derive."0.3.1" { profileName = "__noProfile"; }).out;
|
||||
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.28" { inherit profileName; }).out;
|
||||
futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.28" { inherit profileName; }).out;
|
||||
garage_block = (rustPackages."unknown".garage_block."0.8.4" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."0.8.4" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."0.8.4" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."0.8.4" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."0.8.4" { inherit profileName; }).out;
|
||||
garage_block = (rustPackages."unknown".garage_block."0.8.5" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."0.8.5" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."0.8.5" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."0.8.5" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."0.8.5" { inherit profileName; }).out;
|
||||
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
|
||||
netapp = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".netapp."0.5.2" { inherit profileName; }).out;
|
||||
opentelemetry = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".opentelemetry."0.17.0" { inherit profileName; }).out;
|
||||
@@ -1931,9 +1933,9 @@ in
|
||||
};
|
||||
});
|
||||
|
||||
"unknown".garage_rpc."0.8.4" = overridableMkRustCrate (profileName: rec {
|
||||
"unknown".garage_rpc."0.8.5" = overridableMkRustCrate (profileName: rec {
|
||||
name = "garage_rpc";
|
||||
version = "0.8.4";
|
||||
version = "0.8.5";
|
||||
registry = "unknown";
|
||||
src = fetchCrateLocal (workspaceSrc + "/src/rpc");
|
||||
features = builtins.concatLists [
|
||||
@@ -1953,8 +1955,8 @@ in
|
||||
${ if rootFeatures' ? "garage/consul-discovery" || rootFeatures' ? "garage_rpc/consul-discovery" || rootFeatures' ? "garage_rpc/err-derive" then "err_derive" else null } = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".err-derive."0.3.1" { profileName = "__noProfile"; }).out;
|
||||
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.28" { inherit profileName; }).out;
|
||||
futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.28" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."0.8.4" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."0.8.4" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."0.8.5" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."0.8.5" { inherit profileName; }).out;
|
||||
gethostname = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".gethostname."0.4.3" { inherit profileName; }).out;
|
||||
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
|
||||
${ if rootFeatures' ? "garage/kubernetes-discovery" || rootFeatures' ? "garage_rpc/k8s-openapi" || rootFeatures' ? "garage_rpc/kubernetes-discovery" then "k8s_openapi" else null } = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".k8s-openapi."0.16.0" { inherit profileName; }).out;
|
||||
@@ -1976,9 +1978,9 @@ in
|
||||
};
|
||||
});
|
||||
|
||||
"unknown".garage_table."0.8.4" = overridableMkRustCrate (profileName: rec {
|
||||
"unknown".garage_table."0.8.5" = overridableMkRustCrate (profileName: rec {
|
||||
name = "garage_table";
|
||||
version = "0.8.4";
|
||||
version = "0.8.5";
|
||||
registry = "unknown";
|
||||
src = fetchCrateLocal (workspaceSrc + "/src/table");
|
||||
dependencies = {
|
||||
@@ -1987,9 +1989,9 @@ in
|
||||
bytes = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".bytes."1.4.0" { inherit profileName; }).out;
|
||||
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.28" { inherit profileName; }).out;
|
||||
futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.28" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."0.8.4" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."0.8.4" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."0.8.4" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."0.8.5" { inherit profileName; }).out;
|
||||
garage_rpc = (rustPackages."unknown".garage_rpc."0.8.5" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."0.8.5" { inherit profileName; }).out;
|
||||
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
|
||||
hexdump = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hexdump."0.1.1" { inherit profileName; }).out;
|
||||
opentelemetry = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".opentelemetry."0.17.0" { inherit profileName; }).out;
|
||||
@@ -2001,9 +2003,9 @@ in
|
||||
};
|
||||
});
|
||||
|
||||
"unknown".garage_util."0.8.4" = overridableMkRustCrate (profileName: rec {
|
||||
"unknown".garage_util."0.8.5" = overridableMkRustCrate (profileName: rec {
|
||||
name = "garage_util";
|
||||
version = "0.8.4";
|
||||
version = "0.8.5";
|
||||
registry = "unknown";
|
||||
src = fetchCrateLocal (workspaceSrc + "/src/util");
|
||||
features = builtins.concatLists [
|
||||
@@ -2019,7 +2021,7 @@ in
|
||||
digest = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".digest."0.10.7" { inherit profileName; }).out;
|
||||
err_derive = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".err-derive."0.3.1" { profileName = "__noProfile"; }).out;
|
||||
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.28" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."0.8.4" { inherit profileName; }).out;
|
||||
garage_db = (rustPackages."unknown".garage_db."0.8.5" { inherit profileName; }).out;
|
||||
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
|
||||
hexdump = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hexdump."0.1.1" { inherit profileName; }).out;
|
||||
http = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".http."0.2.9" { inherit profileName; }).out;
|
||||
@@ -2045,22 +2047,24 @@ in
|
||||
};
|
||||
});
|
||||
|
||||
"unknown".garage_web."0.8.4" = overridableMkRustCrate (profileName: rec {
|
||||
"unknown".garage_web."0.8.5" = overridableMkRustCrate (profileName: rec {
|
||||
name = "garage_web";
|
||||
version = "0.8.4";
|
||||
version = "0.8.5";
|
||||
registry = "unknown";
|
||||
src = fetchCrateLocal (workspaceSrc + "/src/web");
|
||||
dependencies = {
|
||||
err_derive = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".err-derive."0.3.1" { profileName = "__noProfile"; }).out;
|
||||
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.28" { inherit profileName; }).out;
|
||||
garage_api = (rustPackages."unknown".garage_api."0.8.4" { inherit profileName; }).out;
|
||||
garage_model = (rustPackages."unknown".garage_model."0.8.4" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."0.8.4" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."0.8.4" { inherit profileName; }).out;
|
||||
garage_api = (rustPackages."unknown".garage_api."0.8.5" { inherit profileName; }).out;
|
||||
garage_model = (rustPackages."unknown".garage_model."0.8.5" { inherit profileName; }).out;
|
||||
garage_table = (rustPackages."unknown".garage_table."0.8.5" { inherit profileName; }).out;
|
||||
garage_util = (rustPackages."unknown".garage_util."0.8.5" { inherit profileName; }).out;
|
||||
http = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".http."0.2.9" { inherit profileName; }).out;
|
||||
hyper = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hyper."0.14.27" { inherit profileName; }).out;
|
||||
hyperlocal = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hyperlocal."0.8.0" { inherit profileName; }).out;
|
||||
opentelemetry = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".opentelemetry."0.17.0" { inherit profileName; }).out;
|
||||
percent_encoding = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".percent-encoding."2.3.0" { inherit profileName; }).out;
|
||||
tokio = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio."1.32.0" { inherit profileName; }).out;
|
||||
tracing = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".tracing."0.1.37" { inherit profileName; }).out;
|
||||
};
|
||||
});
|
||||
@@ -2486,6 +2490,23 @@ in
|
||||
};
|
||||
});
|
||||
|
||||
"registry+https://github.com/rust-lang/crates.io-index".hyperlocal."0.8.0" = overridableMkRustCrate (profileName: rec {
|
||||
name = "hyperlocal";
|
||||
version = "0.8.0";
|
||||
registry = "registry+https://github.com/rust-lang/crates.io-index";
|
||||
src = fetchCratesIo { inherit name version; sha256 = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c"; };
|
||||
features = builtins.concatLists [
|
||||
[ "server" ]
|
||||
];
|
||||
dependencies = {
|
||||
futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.28" { inherit profileName; }).out;
|
||||
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
|
||||
hyper = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hyper."0.14.27" { inherit profileName; }).out;
|
||||
pin_project = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".pin-project."1.1.3" { inherit profileName; }).out;
|
||||
tokio = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio."1.32.0" { inherit profileName; }).out;
|
||||
};
|
||||
});
|
||||
|
||||
"registry+https://github.com/rust-lang/crates.io-index".iana-time-zone."0.1.57" = overridableMkRustCrate (profileName: rec {
|
||||
name = "iana-time-zone";
|
||||
version = "0.1.57";
|
||||
|
||||
+8
-8
@@ -18,14 +18,14 @@ default-members = ["src/garage"]
|
||||
|
||||
[workspace.dependencies]
|
||||
format_table = { version = "0.1.1", path = "src/format-table" }
|
||||
garage_api = { version = "0.8.4", path = "src/api" }
|
||||
garage_block = { version = "0.8.4", path = "src/block" }
|
||||
garage_db = { version = "0.8.4", path = "src/db", default-features = false }
|
||||
garage_model = { version = "0.8.4", path = "src/model", default-features = false }
|
||||
garage_rpc = { version = "0.8.4", path = "src/rpc" }
|
||||
garage_table = { version = "0.8.4", path = "src/table" }
|
||||
garage_util = { version = "0.8.4", path = "src/util" }
|
||||
garage_web = { version = "0.8.4", path = "src/web" }
|
||||
garage_api = { version = "0.8.5", path = "src/api" }
|
||||
garage_block = { version = "0.8.5", path = "src/block" }
|
||||
garage_db = { version = "0.8.5", path = "src/db", default-features = false }
|
||||
garage_model = { version = "0.8.5", path = "src/model", default-features = false }
|
||||
garage_rpc = { version = "0.8.5", path = "src/rpc" }
|
||||
garage_table = { version = "0.8.5", path = "src/table" }
|
||||
garage_util = { version = "0.8.5", path = "src/util" }
|
||||
garage_web = { version = "0.8.5", path = "src/web" }
|
||||
k2v-client = { version = "0.0.4", path = "src/k2v-client" }
|
||||
|
||||
[profile.dev]
|
||||
|
||||
@@ -421,7 +421,7 @@ Now we can write a simple script (eg `~/.local/bin/matrix-cache-gc`):
|
||||
## CONFIGURATION ##
|
||||
AWS_ACCESS_KEY_ID=GKxxx
|
||||
AWS_SECRET_ACCESS_KEY=xxxx
|
||||
S3_ENDPOINT=http://localhost:3900
|
||||
AWS_ENDPOINT_URL=http://localhost:3900
|
||||
S3_BUCKET=matrix
|
||||
MEDIA_STORE=/var/lib/matrix-synapse/media
|
||||
PG_USER=matrix
|
||||
@@ -442,7 +442,7 @@ EOF
|
||||
|
||||
s3_media_upload update-db 1d
|
||||
s3_media_upload --no-progress check-deleted $MEDIA_STORE
|
||||
s3_media_upload --no-progress upload $MEDIA_STORE $S3_BUCKET --delete --endpoint-url $S3_ENDPOINT
|
||||
s3_media_upload --no-progress upload $MEDIA_STORE $S3_BUCKET --delete --endpoint-url $AWS_ENDPOINT_URL
|
||||
```
|
||||
|
||||
This script will list all the medias that were not accessed in the 24 hours according to your database.
|
||||
|
||||
@@ -70,16 +70,17 @@ Then a file named `~/.aws/config` and put:
|
||||
```toml
|
||||
[default]
|
||||
region=garage
|
||||
endpoint_url=http://127.0.0.1:3900
|
||||
```
|
||||
|
||||
Now, supposing Garage is listening on `http://127.0.0.1:3900`, you can list your buckets with:
|
||||
|
||||
```bash
|
||||
aws --endpoint-url http://127.0.0.1:3900 s3 ls
|
||||
aws s3 ls
|
||||
```
|
||||
|
||||
Passing the `--endpoint-url` parameter to each command is annoying but AWS developers do not provide a corresponding configuration entry.
|
||||
As a workaround, you can redefine the aws command by editing the file `~/.bashrc`:
|
||||
If you're using awscli `<1.29.0` or `<2.13.0`, you need to pass `--endpoint-url` to each CLI invocation explicitly.
|
||||
As a workaround, you can redefine the aws command by editing the file `~/.bashrc` in this case:
|
||||
|
||||
```
|
||||
function aws { command aws --endpoint-url http://127.0.0.1:3900 $@ ; }
|
||||
|
||||
@@ -269,12 +269,14 @@ named `~/.awsrc` with this content:
|
||||
export AWS_ACCESS_KEY_ID=xxxx # put your Key ID here
|
||||
export AWS_SECRET_ACCESS_KEY=xxxx # put your Secret key here
|
||||
export AWS_DEFAULT_REGION='garage'
|
||||
export AWS_ENDPOINT='http://localhost:3900'
|
||||
export AWS_ENDPOINT_URL='http://localhost:3900'
|
||||
|
||||
function aws { command aws --endpoint-url $AWS_ENDPOINT $@ ; }
|
||||
aws --version
|
||||
```
|
||||
|
||||
Note you need to have at least `awscli` `>=1.29.0` or `>=2.13.0`, otherwise you
|
||||
need to specify `--endpoint-url` explicitly on each `awscli` invocation.
|
||||
|
||||
Now, each time you want to use `awscli` on this target, run:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -276,7 +276,7 @@ Compression is done synchronously, setting a value too high will add latency to
|
||||
This value can be different between nodes, compression is done by the node which receive the
|
||||
API call.
|
||||
|
||||
### `rpc_secret`, `rpc_secret_file` or `GARAGE_RPC_SECRET` (env)
|
||||
### `rpc_secret`, `rpc_secret_file` or `GARAGE_RPC_SECRET`, `GARAGE_RPC_SECRET_FILE` (env)
|
||||
|
||||
Garage uses a secret key, called an RPC secret, that is shared between all
|
||||
nodes of the cluster in order to identify these nodes and allow them to
|
||||
@@ -288,6 +288,9 @@ Since Garage `v0.8.2`, the RPC secret can also be stored in a file whose path is
|
||||
given in the configuration variable `rpc_secret_file`, or specified as an
|
||||
environment variable `GARAGE_RPC_SECRET`.
|
||||
|
||||
Since Garage `v0.9.0`, you can also specify the path of a file storing the secret
|
||||
as the `GARAGE_RPC_SECRET_FILE` environment variable.
|
||||
|
||||
### `rpc_bind_addr`
|
||||
|
||||
The address and port on which to bind for inter-cluster communcations
|
||||
@@ -320,6 +323,17 @@ be obtained by running `garage node id` and then included directly in the
|
||||
key will be returned by `garage node id` and you will have to add the IP
|
||||
yourself.
|
||||
|
||||
### `allow_world_readable_secrets`
|
||||
|
||||
Garage checks the permissions of your secret files to make sure they're not
|
||||
world-readable. In some cases, the check might fail and consider your files as
|
||||
world-readable even if they're not, for instance when using Posix ACLs.
|
||||
|
||||
Setting `allow_world_readable_secrets` to `true` bypass this
|
||||
permission verification.
|
||||
|
||||
Alternatively, you can set the `GARAGE_ALLOW_WORLD_READABLE_SECRETS`
|
||||
environment variable to `true` to bypass the permissions check.
|
||||
|
||||
## The `[consul_discovery]` section
|
||||
|
||||
@@ -410,6 +424,8 @@ manually.
|
||||
The IP and port on which to bind for accepting S3 API calls.
|
||||
This endpoint does not suport TLS: a reverse proxy should be used to provide it.
|
||||
|
||||
Alternatively, since `v0.8.5`, a path can be used to create a unix socket with 0222 mode.
|
||||
|
||||
### `s3_region`
|
||||
|
||||
Garage will accept S3 API calls that are targetted to the S3 region defined here.
|
||||
@@ -439,6 +455,8 @@ The IP and port on which to bind for accepting HTTP requests to buckets configur
|
||||
for website access.
|
||||
This endpoint does not suport TLS: a reverse proxy should be used to provide it.
|
||||
|
||||
Alternatively, since `v0.8.5`, a path can be used to create a unix socket with 0222 mode.
|
||||
|
||||
### `root_domain`
|
||||
|
||||
The optional suffix appended to bucket names for the corresponding HTTP Host.
|
||||
@@ -458,7 +476,10 @@ If specified, Garage will bind an HTTP server to this port and address, on
|
||||
which it will listen to requests for administration features.
|
||||
See [administration API reference](@/documentation/reference-manual/admin-api.md) to learn more about these features.
|
||||
|
||||
### `metrics_token`, `metrics_token_file` or `GARAGE_METRICS_TOKEN` (env)
|
||||
Alternatively, since `v0.8.5`, a path can be used to create a unix socket. Note that for security reasons,
|
||||
the socket will have 0220 mode. Make sure to set user and group permissions accordingly.
|
||||
|
||||
### `metrics_token`, `metrics_token_file` or `GARAGE_METRICS_TOKEN`, `GARAGE_METRICS_TOKEN_FILE` (env)
|
||||
|
||||
The token for accessing the Metrics endpoint. If this token is not set, the
|
||||
Metrics endpoint can be accessed without access control.
|
||||
@@ -468,8 +489,9 @@ You can use any random string for this value. We recommend generating a random t
|
||||
`metrics_token` was introduced in Garage `v0.7.2`.
|
||||
`metrics_token_file` and the `GARAGE_METRICS_TOKEN` environment variable are supported since Garage `v0.8.2`.
|
||||
|
||||
`GARAGE_METRICS_TOKEN_FILE` is supported since `v0.9.0`.
|
||||
|
||||
### `admin_token`, `admin_token_file` or `GARAGE_ADMIN_TOKEN` (env)
|
||||
### `admin_token`, `admin_token_file` or `GARAGE_ADMIN_TOKEN`, `GARAGE_ADMIN_TOKEN_FILE` (env)
|
||||
|
||||
The token for accessing all of the other administration endpoints. If this
|
||||
token is not set, access to these endpoints is disabled entirely.
|
||||
@@ -479,6 +501,7 @@ You can use any random string for this value. We recommend generating a random t
|
||||
`admin_token` was introduced in Garage `v0.7.2`.
|
||||
`admin_token_file` and the `GARAGE_ADMIN_TOKEN` environment variable are supported since Garage `v0.8.2`.
|
||||
|
||||
`GARAGE_ADMIN_TOKEN_FILE` is supported since `v0.9.0`.
|
||||
|
||||
### `trace_sink`
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export AWS_ACCESS_KEY_ID=`cat /tmp/garage.s3 |cut -d' ' -f1`
|
||||
export AWS_SECRET_ACCESS_KEY=`cat /tmp/garage.s3 |cut -d' ' -f2`
|
||||
export AWS_DEFAULT_REGION='garage'
|
||||
|
||||
# FUTUREWORK: set AWS_ENDPOINT_URL instead, once nixpkgs bumps awscli to >=2.13.0.
|
||||
function aws { command aws --endpoint-url http://127.0.0.1:3911 $@ ; }
|
||||
|
||||
aws --version
|
||||
|
||||
@@ -21,4 +21,4 @@ version: 0.4.1
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: "v0.8.4"
|
||||
appVersion: "v0.8.5"
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "garage_api"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0"
|
||||
@@ -45,6 +45,7 @@ http = "0.2"
|
||||
httpdate = "1.0"
|
||||
http-range = "0.1"
|
||||
hyper = { version = "0.14", features = ["server", "http1", "runtime", "tcp", "stream"] }
|
||||
hyperlocal = { version = "0.8.0", default-features = false, features = ["server"] }
|
||||
multer = "2.0"
|
||||
percent-encoding = "2.1.0"
|
||||
roxmltree = "0.18"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
@@ -18,6 +17,7 @@ use prometheus::{Encoder, TextEncoder};
|
||||
use garage_model::garage::Garage;
|
||||
use garage_rpc::system::ClusterHealthStatus;
|
||||
use garage_util::error::Error as GarageError;
|
||||
use garage_util::socket_address::UnixOrTCPSocketAddress;
|
||||
|
||||
use crate::generic_server::*;
|
||||
|
||||
@@ -61,12 +61,12 @@ impl AdminApiServer {
|
||||
|
||||
pub async fn run(
|
||||
self,
|
||||
bind_addr: SocketAddr,
|
||||
bind_addr: UnixOrTCPSocketAddress,
|
||||
shutdown_signal: impl Future<Output = ()>,
|
||||
) -> Result<(), GarageError> {
|
||||
let region = self.garage.config.s3_api.s3_region.clone();
|
||||
ApiServer::new(region, self)
|
||||
.run_server(bind_addr, shutdown_signal)
|
||||
.run_server(bind_addr, Some(0o220), shutdown_signal)
|
||||
.await
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::net::SocketAddr;
|
||||
use std::fs::{self, Permissions};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
@@ -11,6 +12,10 @@ use hyper::service::{make_service_fn, service_fn};
|
||||
use hyper::{Body, Request, Response, Server};
|
||||
use hyper::{HeaderMap, StatusCode};
|
||||
|
||||
use hyperlocal::UnixServerExt;
|
||||
|
||||
use tokio::net::UnixStream;
|
||||
|
||||
use opentelemetry::{
|
||||
global,
|
||||
metrics::{Counter, ValueRecorder},
|
||||
@@ -21,6 +26,7 @@ use opentelemetry::{
|
||||
use garage_util::error::Error as GarageError;
|
||||
use garage_util::forwarded_headers;
|
||||
use garage_util::metrics::{gen_trace_id, RecordDuration};
|
||||
use garage_util::socket_address::UnixOrTCPSocketAddress;
|
||||
|
||||
pub(crate) trait ApiEndpoint: Send + Sync + 'static {
|
||||
fn name(&self) -> &'static str;
|
||||
@@ -91,10 +97,11 @@ impl<A: ApiHandler> ApiServer<A> {
|
||||
|
||||
pub async fn run_server(
|
||||
self: Arc<Self>,
|
||||
bind_addr: SocketAddr,
|
||||
bind_addr: UnixOrTCPSocketAddress,
|
||||
unix_bind_addr_mode: Option<u32>,
|
||||
shutdown_signal: impl Future<Output = ()>,
|
||||
) -> Result<(), GarageError> {
|
||||
let service = make_service_fn(|conn: &AddrStream| {
|
||||
let tcp_service = make_service_fn(|conn: &AddrStream| {
|
||||
let this = self.clone();
|
||||
|
||||
let client_addr = conn.remote_addr();
|
||||
@@ -102,28 +109,63 @@ impl<A: ApiHandler> ApiServer<A> {
|
||||
Ok::<_, GarageError>(service_fn(move |req: Request<Body>| {
|
||||
let this = this.clone();
|
||||
|
||||
this.handler(req, client_addr)
|
||||
this.handler(req, client_addr.to_string())
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
||||
let server = Server::bind(&bind_addr).serve(service);
|
||||
let unix_service = make_service_fn(|_: &UnixStream| {
|
||||
let this = self.clone();
|
||||
|
||||
let path = bind_addr.to_string();
|
||||
async move {
|
||||
Ok::<_, GarageError>(service_fn(move |req: Request<Body>| {
|
||||
let this = this.clone();
|
||||
|
||||
this.handler(req, path.clone())
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
||||
let graceful = server.with_graceful_shutdown(shutdown_signal);
|
||||
info!(
|
||||
"{} API server listening on http://{}",
|
||||
"{} API server listening on {}",
|
||||
A::API_NAME_DISPLAY,
|
||||
bind_addr
|
||||
);
|
||||
|
||||
graceful.await?;
|
||||
match bind_addr {
|
||||
UnixOrTCPSocketAddress::TCPSocket(addr) => {
|
||||
Server::bind(&addr)
|
||||
.serve(tcp_service)
|
||||
.with_graceful_shutdown(shutdown_signal)
|
||||
.await?
|
||||
}
|
||||
UnixOrTCPSocketAddress::UnixSocket(ref path) => {
|
||||
if path.exists() {
|
||||
fs::remove_file(path)?
|
||||
}
|
||||
|
||||
let bound = Server::bind_unix(path)?;
|
||||
|
||||
fs::set_permissions(
|
||||
path,
|
||||
Permissions::from_mode(unix_bind_addr_mode.unwrap_or(0o222)),
|
||||
)?;
|
||||
|
||||
bound
|
||||
.serve(unix_service)
|
||||
.with_graceful_shutdown(shutdown_signal)
|
||||
.await?;
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handler(
|
||||
self: Arc<Self>,
|
||||
req: Request<Body>,
|
||||
addr: SocketAddr,
|
||||
addr: String,
|
||||
) -> Result<Response<Body>, GarageError> {
|
||||
let uri = req.uri().clone();
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
@@ -9,6 +8,7 @@ use hyper::{Body, Method, Request, Response};
|
||||
use opentelemetry::{trace::SpanRef, KeyValue};
|
||||
|
||||
use garage_util::error::Error as GarageError;
|
||||
use garage_util::socket_address::UnixOrTCPSocketAddress;
|
||||
|
||||
use garage_model::garage::Garage;
|
||||
|
||||
@@ -37,12 +37,12 @@ pub(crate) struct K2VApiEndpoint {
|
||||
impl K2VApiServer {
|
||||
pub async fn run(
|
||||
garage: Arc<Garage>,
|
||||
bind_addr: SocketAddr,
|
||||
bind_addr: UnixOrTCPSocketAddress,
|
||||
s3_region: String,
|
||||
shutdown_signal: impl Future<Output = ()>,
|
||||
) -> Result<(), GarageError> {
|
||||
ApiServer::new(s3_region, K2VApiServer { garage })
|
||||
.run_server(bind_addr, shutdown_signal)
|
||||
.run_server(bind_addr, None, shutdown_signal)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
@@ -10,6 +9,7 @@ use hyper::{Body, Request, Response};
|
||||
use opentelemetry::{trace::SpanRef, KeyValue};
|
||||
|
||||
use garage_util::error::Error as GarageError;
|
||||
use garage_util::socket_address::UnixOrTCPSocketAddress;
|
||||
|
||||
use garage_model::garage::Garage;
|
||||
use garage_model::key_table::Key;
|
||||
@@ -44,12 +44,12 @@ pub(crate) struct S3ApiEndpoint {
|
||||
impl S3ApiServer {
|
||||
pub async fn run(
|
||||
garage: Arc<Garage>,
|
||||
addr: SocketAddr,
|
||||
addr: UnixOrTCPSocketAddress,
|
||||
s3_region: String,
|
||||
shutdown_signal: impl Future<Output = ()>,
|
||||
) -> Result<(), GarageError> {
|
||||
ApiServer::new(s3_region, S3ApiServer { garage })
|
||||
.run_server(addr, shutdown_signal)
|
||||
.run_server(addr, None, shutdown_signal)
|
||||
.await
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ use serde::Deserialize;
|
||||
|
||||
use garage_model::garage::Garage;
|
||||
|
||||
use crate::s3::cors::*;
|
||||
use crate::s3::error::*;
|
||||
use crate::s3::put::{get_headers, save_stream};
|
||||
use crate::s3::xml as s3_xml;
|
||||
@@ -242,7 +243,7 @@ pub async fn handle_post_object(
|
||||
|
||||
let etag = format!("\"{}\"", md5);
|
||||
|
||||
let resp = if let Some(mut target) = params
|
||||
let mut resp = if let Some(mut target) = params
|
||||
.get("success_action_redirect")
|
||||
.and_then(|h| h.to_str().ok())
|
||||
.and_then(|u| url::Url::parse(u).ok())
|
||||
@@ -262,8 +263,7 @@ pub async fn handle_post_object(
|
||||
} else {
|
||||
let path = head
|
||||
.uri
|
||||
.into_parts()
|
||||
.path_and_query
|
||||
.path_and_query()
|
||||
.map(|paq| paq.path().to_string())
|
||||
.unwrap_or_else(|| "/".to_string());
|
||||
let authority = head
|
||||
@@ -308,6 +308,13 @@ pub async fn handle_post_object(
|
||||
}
|
||||
};
|
||||
|
||||
let matching_cors_rule =
|
||||
find_matching_cors_rule(&bucket, &Request::from_parts(head, Body::empty()))?;
|
||||
if let Some(rule) = matching_cors_rule {
|
||||
add_cors_headers(&mut resp, rule)
|
||||
.ok_or_internal_error("Invalid bucket CORS configuration")?;
|
||||
}
|
||||
|
||||
Ok(resp)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "garage_block"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0"
|
||||
|
||||
+1
-6
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "garage_db"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0"
|
||||
@@ -11,11 +11,6 @@ readme = "../../README.md"
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "convert"
|
||||
path = "bin/convert.rs"
|
||||
required-features = ["cli"]
|
||||
|
||||
[dependencies]
|
||||
err-derive = "0.3"
|
||||
hexdump = "0.1"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "garage"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0"
|
||||
@@ -67,6 +67,7 @@ chrono = "0.4"
|
||||
http = "0.2"
|
||||
hmac = "0.12"
|
||||
hyper = { version = "0.14", features = ["client", "http1", "runtime"] }
|
||||
mktemp = "0.5"
|
||||
sha2 = "0.10"
|
||||
|
||||
static_init = "1.0"
|
||||
|
||||
@@ -1,39 +1,30 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use structopt::StructOpt;
|
||||
|
||||
use garage_db::*;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
/// K2V command line interface
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct Args {
|
||||
/// Input DB path
|
||||
#[clap(short = 'i')]
|
||||
#[derive(StructOpt, Debug)]
|
||||
pub struct ConvertDbOpt {
|
||||
/// Input database path (not the same as metadata_dir, see
|
||||
/// https://garagehq.deuxfleurs.fr/documentation/reference-manual/configuration/#db-engine-since-v0-8-0)
|
||||
#[structopt(short = "i")]
|
||||
input_path: PathBuf,
|
||||
/// Input DB engine
|
||||
#[clap(short = 'a')]
|
||||
/// Input database engine (sled, lmdb or sqlite; limited by db engines
|
||||
/// enabled in this build)
|
||||
#[structopt(short = "a")]
|
||||
input_engine: String,
|
||||
|
||||
/// Output DB path
|
||||
#[clap(short = 'o')]
|
||||
/// Output database path
|
||||
#[structopt(short = "o")]
|
||||
output_path: PathBuf,
|
||||
/// Output DB engine
|
||||
#[clap(short = 'b')]
|
||||
/// Output database engine
|
||||
#[structopt(short = "b")]
|
||||
output_engine: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
pretty_env_logger::init();
|
||||
|
||||
match do_conversion(args) {
|
||||
Ok(()) => println!("Success!"),
|
||||
Err(e) => eprintln!("Error: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn do_conversion(args: Args) -> Result<()> {
|
||||
pub(crate) fn do_conversion(args: ConvertDbOpt) -> Result<()> {
|
||||
let input = open_db(args.input_path, args.input_engine)?;
|
||||
let output = open_db(args.output_path, args.output_engine)?;
|
||||
output.import(&input)?;
|
||||
@@ -42,14 +33,17 @@ fn do_conversion(args: Args) -> Result<()> {
|
||||
|
||||
fn open_db(path: PathBuf, engine: String) -> Result<Db> {
|
||||
match engine.as_str() {
|
||||
#[cfg(feature = "sled")]
|
||||
"sled" => {
|
||||
let db = sled_adapter::sled::Config::default().path(&path).open()?;
|
||||
Ok(sled_adapter::SledDb::init(db))
|
||||
}
|
||||
#[cfg(feature = "sqlite")]
|
||||
"sqlite" | "sqlite3" | "rusqlite" => {
|
||||
let db = sqlite_adapter::rusqlite::Connection::open(&path)?;
|
||||
Ok(sqlite_adapter::SqliteDb::init(db))
|
||||
}
|
||||
#[cfg(feature = "lmdb")]
|
||||
"lmdb" | "heed" => {
|
||||
std::fs::create_dir_all(&path).map_err(|e| {
|
||||
Error(format!("Unable to create LMDB data directory: {}", e).into())
|
||||
@@ -64,6 +58,8 @@ fn open_db(path: PathBuf, engine: String) -> Result<Db> {
|
||||
.unwrap();
|
||||
Ok(lmdb_adapter::LmdbDb::init(db))
|
||||
}
|
||||
e => Err(Error(format!("Invalid DB engine: {}", e).into())),
|
||||
e => Err(Error(
|
||||
format!("Invalid or unsupported DB engine: {}", e).into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ pub(crate) mod layout;
|
||||
pub(crate) mod structs;
|
||||
pub(crate) mod util;
|
||||
|
||||
pub(crate) mod convert_db;
|
||||
|
||||
pub(crate) use cmd::*;
|
||||
pub(crate) use init::*;
|
||||
pub(crate) use layout::*;
|
||||
|
||||
@@ -3,6 +3,8 @@ use structopt::StructOpt;
|
||||
|
||||
use garage_util::version::garage_version;
|
||||
|
||||
use crate::cli::convert_db;
|
||||
|
||||
#[derive(StructOpt, Debug)]
|
||||
pub enum Command {
|
||||
/// Run Garage server
|
||||
@@ -54,6 +56,10 @@ pub enum Command {
|
||||
/// Low-level debug operations on data blocks
|
||||
#[structopt(name = "block", version = garage_version())]
|
||||
Block(BlockOperation),
|
||||
|
||||
/// Convert metadata db between database engine formats
|
||||
#[structopt(name = "convert-db", version = garage_version())]
|
||||
ConvertDb(convert_db::ConvertDbOpt),
|
||||
}
|
||||
|
||||
#[derive(StructOpt, Debug)]
|
||||
|
||||
+5
-32
@@ -7,6 +7,7 @@ extern crate tracing;
|
||||
mod admin;
|
||||
mod cli;
|
||||
mod repair;
|
||||
mod secrets;
|
||||
mod server;
|
||||
#[cfg(feature = "telemetry-otlp")]
|
||||
mod tracing_setup;
|
||||
@@ -25,7 +26,6 @@ use structopt::StructOpt;
|
||||
use netapp::util::parse_and_resolve_peer_addr;
|
||||
use netapp::NetworkKey;
|
||||
|
||||
use garage_util::config::Config;
|
||||
use garage_util::error::*;
|
||||
|
||||
use garage_rpc::system::*;
|
||||
@@ -35,6 +35,7 @@ use garage_model::helper::error::Error as HelperError;
|
||||
|
||||
use admin::*;
|
||||
use cli::*;
|
||||
use secrets::Secrets;
|
||||
|
||||
#[derive(StructOpt, Debug)]
|
||||
#[structopt(
|
||||
@@ -63,24 +64,6 @@ struct Opt {
|
||||
cmd: Command,
|
||||
}
|
||||
|
||||
#[derive(StructOpt, Debug)]
|
||||
pub struct Secrets {
|
||||
/// RPC secret network key, used to replace rpc_secret in config.toml when running the
|
||||
/// daemon or doing admin operations
|
||||
#[structopt(short = "s", long = "rpc-secret", env = "GARAGE_RPC_SECRET")]
|
||||
pub rpc_secret: Option<String>,
|
||||
|
||||
/// Metrics API authentication token, replaces admin.metrics_token in config.toml when
|
||||
/// running the Garage daemon
|
||||
#[structopt(long = "admin-token", env = "GARAGE_ADMIN_TOKEN")]
|
||||
pub admin_token: Option<String>,
|
||||
|
||||
/// Metrics API authentication token, replaces admin.metrics_token in config.toml when
|
||||
/// running the Garage daemon
|
||||
#[structopt(long = "metrics-token", env = "GARAGE_METRICS_TOKEN")]
|
||||
pub metrics_token: Option<String>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// Initialize version and features info
|
||||
@@ -173,6 +156,9 @@ async fn main() {
|
||||
Command::OfflineRepair(repair_opt) => {
|
||||
repair::offline::offline_repair(opt.config_file, opt.secrets, repair_opt).await
|
||||
}
|
||||
Command::ConvertDb(conv_opt) => {
|
||||
cli::convert_db::do_conversion(conv_opt).map_err(From::from)
|
||||
}
|
||||
Command::Node(NodeOperation::NodeId(node_id_opt)) => {
|
||||
node_id_command(opt.config_file, node_id_opt.quiet)
|
||||
}
|
||||
@@ -255,16 +241,3 @@ async fn cli_command(opt: Opt) -> Result<(), Error> {
|
||||
Ok(x) => Ok(x),
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_secrets(mut config: Config, secrets: Secrets) -> Config {
|
||||
if secrets.rpc_secret.is_some() {
|
||||
config.rpc_secret = secrets.rpc_secret;
|
||||
}
|
||||
if secrets.admin_token.is_some() {
|
||||
config.admin.admin_token = secrets.admin_token;
|
||||
}
|
||||
if secrets.metrics_token.is_some() {
|
||||
config.admin.metrics_token = secrets.metrics_token;
|
||||
}
|
||||
config
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use garage_util::error::*;
|
||||
use garage_model::garage::Garage;
|
||||
|
||||
use crate::cli::structs::*;
|
||||
use crate::{fill_secrets, Secrets};
|
||||
use crate::secrets::{fill_secrets, Secrets};
|
||||
|
||||
pub async fn offline_repair(
|
||||
config_file: PathBuf,
|
||||
@@ -20,7 +20,7 @@ pub async fn offline_repair(
|
||||
}
|
||||
|
||||
info!("Loading configuration...");
|
||||
let config = fill_secrets(read_config(config_file)?, secrets);
|
||||
let config = fill_secrets(read_config(config_file)?, secrets)?;
|
||||
|
||||
info!("Initializing Garage main data store...");
|
||||
let garage = Garage::new(config)?;
|
||||
|
||||
@@ -0,0 +1,318 @@
|
||||
use structopt::StructOpt;
|
||||
|
||||
use garage_util::config::Config;
|
||||
use garage_util::error::Error;
|
||||
|
||||
/// Structure for secret values or paths that are passed as CLI arguments or environment
|
||||
/// variables, instead of in the config file.
|
||||
#[derive(StructOpt, Debug, Default, Clone)]
|
||||
pub struct Secrets {
|
||||
/// Skip permission check on files containing secrets
|
||||
#[cfg(unix)]
|
||||
#[structopt(
|
||||
long = "allow-world-readable-secrets",
|
||||
env = "GARAGE_ALLOW_WORLD_READABLE_SECRETS"
|
||||
)]
|
||||
pub allow_world_readable_secrets: Option<bool>,
|
||||
|
||||
/// RPC secret network key, used to replace rpc_secret in config.toml when running the
|
||||
/// daemon or doing admin operations
|
||||
#[structopt(short = "s", long = "rpc-secret", env = "GARAGE_RPC_SECRET")]
|
||||
pub rpc_secret: Option<String>,
|
||||
|
||||
/// RPC secret network key, used to replace rpc_secret in config.toml and rpc-secret
|
||||
/// when running the daemon or doing admin operations
|
||||
#[structopt(long = "rpc-secret-file", env = "GARAGE_RPC_SECRET_FILE")]
|
||||
pub rpc_secret_file: Option<String>,
|
||||
|
||||
/// Admin API authentication token, replaces admin.admin_token in config.toml when
|
||||
/// running the Garage daemon
|
||||
#[structopt(long = "admin-token", env = "GARAGE_ADMIN_TOKEN")]
|
||||
pub admin_token: Option<String>,
|
||||
|
||||
/// Admin API authentication token file path, replaces admin.admin_token in config.toml
|
||||
/// and admin-token when running the Garage daemon
|
||||
#[structopt(long = "admin-token-file", env = "GARAGE_ADMIN_TOKEN_FILE")]
|
||||
pub admin_token_file: Option<String>,
|
||||
|
||||
/// Metrics API authentication token, replaces admin.metrics_token in config.toml when
|
||||
/// running the Garage daemon
|
||||
#[structopt(long = "metrics-token", env = "GARAGE_METRICS_TOKEN")]
|
||||
pub metrics_token: Option<String>,
|
||||
|
||||
/// Metrics API authentication token file path, replaces admin.metrics_token in config.toml
|
||||
/// and metrics-token when running the Garage daemon
|
||||
#[structopt(long = "metrics-token-file", env = "GARAGE_METRICS_TOKEN_FILE")]
|
||||
pub metrics_token_file: Option<String>,
|
||||
}
|
||||
|
||||
/// Single function to fill all secrets in the Config struct from their correct source (value
|
||||
/// from config or CLI param or env variable or read from a file specified in config or CLI
|
||||
/// param or env variable)
|
||||
pub fn fill_secrets(mut config: Config, secrets: Secrets) -> Result<Config, Error> {
|
||||
let allow_world_readable = secrets
|
||||
.allow_world_readable_secrets
|
||||
.unwrap_or(config.allow_world_readable_secrets);
|
||||
|
||||
fill_secret(
|
||||
&mut config.rpc_secret,
|
||||
&config.rpc_secret_file,
|
||||
&secrets.rpc_secret,
|
||||
&secrets.rpc_secret_file,
|
||||
"rpc_secret",
|
||||
allow_world_readable,
|
||||
)?;
|
||||
|
||||
fill_secret(
|
||||
&mut config.admin.admin_token,
|
||||
&config.admin.admin_token_file,
|
||||
&secrets.admin_token,
|
||||
&secrets.admin_token_file,
|
||||
"admin.admin_token",
|
||||
allow_world_readable,
|
||||
)?;
|
||||
fill_secret(
|
||||
&mut config.admin.metrics_token,
|
||||
&config.admin.metrics_token_file,
|
||||
&secrets.metrics_token,
|
||||
&secrets.metrics_token_file,
|
||||
"admin.metrics_token",
|
||||
allow_world_readable,
|
||||
)?;
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn fill_secret(
|
||||
config_secret: &mut Option<String>,
|
||||
config_secret_file: &Option<String>,
|
||||
cli_secret: &Option<String>,
|
||||
cli_secret_file: &Option<String>,
|
||||
name: &'static str,
|
||||
allow_world_readable: bool,
|
||||
) -> Result<(), Error> {
|
||||
let cli_value = match (&cli_secret, &cli_secret_file) {
|
||||
(Some(_), Some(_)) => {
|
||||
return Err(format!("only one of `{}` and `{}_file` can be set", name, name).into());
|
||||
}
|
||||
(Some(secret), None) => Some(secret.to_string()),
|
||||
(None, Some(file)) => Some(read_secret_file(file, allow_world_readable)?),
|
||||
(None, None) => None,
|
||||
};
|
||||
|
||||
if let Some(val) = cli_value {
|
||||
if config_secret.is_some() || config_secret_file.is_some() {
|
||||
debug!("Overriding secret `{}` using value specified using CLI argument or environnement variable.", name);
|
||||
}
|
||||
|
||||
*config_secret = Some(val);
|
||||
} else if let Some(file_path) = &config_secret_file {
|
||||
if config_secret.is_some() {
|
||||
return Err(format!("only one of `{}` and `{}_file` can be set", name, name).into());
|
||||
}
|
||||
|
||||
*config_secret = Some(read_secret_file(file_path, allow_world_readable)?);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_secret_file(file_path: &String, allow_world_readable: bool) -> Result<String, Error> {
|
||||
if !allow_world_readable {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
let metadata = std::fs::metadata(file_path)?;
|
||||
if metadata.mode() & 0o077 != 0 {
|
||||
return Err(format!("File {} is world-readable! (mode: 0{:o}, expected 0600)\nRefusing to start until this is fixed, or environment variable GARAGE_ALLOW_WORLD_READABLE_SECRETS is set to true.", file_path, metadata.mode()).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let secret_buf = std::fs::read_to_string(file_path)?;
|
||||
|
||||
// trim_end: allows for use case such as `echo "$(openssl rand -hex 32)" > somefile`.
|
||||
// also editors sometimes add a trailing newline
|
||||
Ok(String::from(secret_buf.trim_end()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
use garage_util::config::read_config;
|
||||
use garage_util::error::Error;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_rpc_secret_file_works() -> Result<(), Error> {
|
||||
let path_secret = mktemp::Temp::new_file()?;
|
||||
let mut file_secret = File::create(path_secret.as_path())?;
|
||||
writeln!(file_secret, "foo")?;
|
||||
drop(file_secret);
|
||||
|
||||
let path_config = mktemp::Temp::new_file()?;
|
||||
let mut file_config = File::create(path_config.as_path())?;
|
||||
let path_secret_path = path_secret.as_path();
|
||||
writeln!(
|
||||
file_config,
|
||||
r#"
|
||||
metadata_dir = "/tmp/garage/meta"
|
||||
data_dir = "/tmp/garage/data"
|
||||
replication_mode = "3"
|
||||
rpc_bind_addr = "[::]:3901"
|
||||
rpc_secret_file = "{}"
|
||||
|
||||
[s3_api]
|
||||
s3_region = "garage"
|
||||
api_bind_addr = "[::]:3900"
|
||||
"#,
|
||||
path_secret_path.display()
|
||||
)?;
|
||||
drop(file_config);
|
||||
|
||||
// Second configuration file, same as previous one
|
||||
// except it allows world-readable secrets.
|
||||
let path_config_allow_world_readable = mktemp::Temp::new_file()?;
|
||||
let mut file_config_allow_world_readable =
|
||||
File::create(path_config_allow_world_readable.as_path())?;
|
||||
writeln!(
|
||||
file_config_allow_world_readable,
|
||||
r#"
|
||||
metadata_dir = "/tmp/garage/meta"
|
||||
data_dir = "/tmp/garage/data"
|
||||
replication_mode = "3"
|
||||
rpc_bind_addr = "[::]:3901"
|
||||
rpc_secret_file = "{}"
|
||||
allow_world_readable_secrets = true
|
||||
|
||||
[s3_api]
|
||||
s3_region = "garage"
|
||||
api_bind_addr = "[::]:3900"
|
||||
"#,
|
||||
path_secret_path.display()
|
||||
)?;
|
||||
drop(file_config_allow_world_readable);
|
||||
|
||||
let config = read_config(path_config.to_path_buf())?;
|
||||
let config = fill_secrets(config, Secrets::default())?;
|
||||
assert_eq!("foo", config.rpc_secret.unwrap());
|
||||
|
||||
// ---- Check non world-readable secrets config ----
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let secrets_allow_world_readable = Secrets {
|
||||
allow_world_readable_secrets: Some(true),
|
||||
..Default::default()
|
||||
};
|
||||
let secrets_no_allow_world_readable = Secrets {
|
||||
allow_world_readable_secrets: Some(false),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let metadata = std::fs::metadata(&path_secret_path)?;
|
||||
let mut perm = metadata.permissions();
|
||||
perm.set_mode(0o660);
|
||||
std::fs::set_permissions(&path_secret_path, perm)?;
|
||||
|
||||
// Config file that just specifies the path
|
||||
let config = read_config(path_config.to_path_buf())?;
|
||||
assert!(fill_secrets(config, Secrets::default()).is_err());
|
||||
|
||||
let config = read_config(path_config.to_path_buf())?;
|
||||
assert!(fill_secrets(config, secrets_allow_world_readable.clone()).is_ok());
|
||||
|
||||
let config = read_config(path_config.to_path_buf())?;
|
||||
assert!(fill_secrets(config, secrets_no_allow_world_readable.clone()).is_err());
|
||||
|
||||
// Config file that also specifies to allow world_readable_secrets
|
||||
let config = read_config(path_config_allow_world_readable.to_path_buf())?;
|
||||
assert!(fill_secrets(config, Secrets::default()).is_ok());
|
||||
|
||||
let config = read_config(path_config_allow_world_readable.to_path_buf())?;
|
||||
assert!(fill_secrets(config, secrets_allow_world_readable).is_ok());
|
||||
|
||||
let config = read_config(path_config_allow_world_readable.to_path_buf())?;
|
||||
assert!(fill_secrets(config, secrets_no_allow_world_readable).is_err());
|
||||
}
|
||||
|
||||
// ---- Check alternative secrets specified on CLI ----
|
||||
|
||||
let path_secret2 = mktemp::Temp::new_file()?;
|
||||
let mut file_secret2 = File::create(path_secret2.as_path())?;
|
||||
writeln!(file_secret2, "bar")?;
|
||||
drop(file_secret2);
|
||||
|
||||
let config = read_config(path_config.to_path_buf())?;
|
||||
let config = fill_secrets(
|
||||
config,
|
||||
Secrets {
|
||||
rpc_secret: Some("baz".into()),
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
assert_eq!(config.rpc_secret.as_deref(), Some("baz"));
|
||||
|
||||
let config = read_config(path_config.to_path_buf())?;
|
||||
let config = fill_secrets(
|
||||
config,
|
||||
Secrets {
|
||||
rpc_secret_file: Some(path_secret2.display().to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
assert_eq!(config.rpc_secret.as_deref(), Some("bar"));
|
||||
|
||||
let config = read_config(path_config.to_path_buf())?;
|
||||
assert!(fill_secrets(
|
||||
config,
|
||||
Secrets {
|
||||
rpc_secret: Some("baz".into()),
|
||||
rpc_secret_file: Some(path_secret2.display().to_string()),
|
||||
..Default::default()
|
||||
}
|
||||
)
|
||||
.is_err());
|
||||
|
||||
drop(path_secret);
|
||||
drop(path_secret2);
|
||||
drop(path_config);
|
||||
drop(path_config_allow_world_readable);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rcp_secret_and_rpc_secret_file_cannot_be_set_both() -> Result<(), Error> {
|
||||
let path_config = mktemp::Temp::new_file()?;
|
||||
let mut file_config = File::create(path_config.as_path())?;
|
||||
writeln!(
|
||||
file_config,
|
||||
r#"
|
||||
metadata_dir = "/tmp/garage/meta"
|
||||
data_dir = "/tmp/garage/data"
|
||||
replication_mode = "3"
|
||||
rpc_bind_addr = "[::]:3901"
|
||||
rpc_secret= "dummy"
|
||||
rpc_secret_file = "dummy"
|
||||
|
||||
[s3_api]
|
||||
s3_region = "garage"
|
||||
api_bind_addr = "[::]:3900"
|
||||
"#
|
||||
)?;
|
||||
let config = read_config(path_config.to_path_buf())?;
|
||||
assert_eq!(
|
||||
"only one of `rpc_secret` and `rpc_secret_file` can be set",
|
||||
fill_secrets(config, Secrets::default())
|
||||
.unwrap_err()
|
||||
.to_string()
|
||||
);
|
||||
drop(path_config);
|
||||
drop(file_config);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+16
-7
@@ -15,9 +15,9 @@ use garage_web::WebServer;
|
||||
use garage_api::k2v::api_server::K2VApiServer;
|
||||
|
||||
use crate::admin::*;
|
||||
use crate::secrets::{fill_secrets, Secrets};
|
||||
#[cfg(feature = "telemetry-otlp")]
|
||||
use crate::tracing_setup::*;
|
||||
use crate::{fill_secrets, Secrets};
|
||||
|
||||
async fn wait_from(mut chan: watch::Receiver<bool>) {
|
||||
while !*chan.borrow() {
|
||||
@@ -29,12 +29,19 @@ async fn wait_from(mut chan: watch::Receiver<bool>) {
|
||||
|
||||
pub async fn run_server(config_file: PathBuf, secrets: Secrets) -> Result<(), Error> {
|
||||
info!("Loading configuration...");
|
||||
let config = fill_secrets(read_config(config_file)?, secrets);
|
||||
let config = fill_secrets(read_config(config_file)?, secrets)?;
|
||||
|
||||
// ---- Initialize Garage internals ----
|
||||
|
||||
#[cfg(feature = "metrics")]
|
||||
let metrics_exporter = opentelemetry_prometheus::exporter().init();
|
||||
let metrics_exporter = opentelemetry_prometheus::exporter()
|
||||
.with_default_summary_quantiles(vec![0.25, 0.5, 0.75, 0.9, 0.95, 0.99])
|
||||
.with_default_histogram_boundaries(vec![
|
||||
0.001, 0.0015, 0.002, 0.003, 0.005, 0.007, 0.01, 0.015, 0.02, 0.03, 0.05, 0.07, 0.1,
|
||||
0.15, 0.2, 0.3, 0.5, 0.7, 1., 1.5, 2., 3., 5., 7., 10., 15., 20., 30., 40., 50., 60.,
|
||||
70., 100.,
|
||||
])
|
||||
.init();
|
||||
|
||||
info!("Initializing Garage main data store...");
|
||||
let garage = Garage::new(config.clone())?;
|
||||
@@ -79,7 +86,7 @@ pub async fn run_server(config_file: PathBuf, secrets: Secrets) -> Result<(), Er
|
||||
"S3 API",
|
||||
tokio::spawn(S3ApiServer::run(
|
||||
garage.clone(),
|
||||
*s3_bind_addr,
|
||||
s3_bind_addr.clone(),
|
||||
config.s3_api.s3_region.clone(),
|
||||
wait_from(watch_cancel.clone()),
|
||||
)),
|
||||
@@ -94,7 +101,7 @@ pub async fn run_server(config_file: PathBuf, secrets: Secrets) -> Result<(), Er
|
||||
"K2V API",
|
||||
tokio::spawn(K2VApiServer::run(
|
||||
garage.clone(),
|
||||
config.k2v_api.as_ref().unwrap().api_bind_addr,
|
||||
config.k2v_api.as_ref().unwrap().api_bind_addr.clone(),
|
||||
config.s3_api.s3_region.clone(),
|
||||
wait_from(watch_cancel.clone()),
|
||||
)),
|
||||
@@ -110,7 +117,7 @@ pub async fn run_server(config_file: PathBuf, secrets: Secrets) -> Result<(), Er
|
||||
"Web",
|
||||
tokio::spawn(WebServer::run(
|
||||
garage.clone(),
|
||||
web_config.bind_addr,
|
||||
web_config.bind_addr.clone(),
|
||||
web_config.root_domain.clone(),
|
||||
wait_from(watch_cancel.clone()),
|
||||
)),
|
||||
@@ -121,7 +128,9 @@ pub async fn run_server(config_file: PathBuf, secrets: Secrets) -> Result<(), Er
|
||||
info!("Launching Admin API server...");
|
||||
servers.push((
|
||||
"Admin",
|
||||
tokio::spawn(admin_server.run(*admin_bind_addr, wait_from(watch_cancel.clone()))),
|
||||
tokio::spawn(
|
||||
admin_server.run(admin_bind_addr.clone(), wait_from(watch_cancel.clone())),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "garage_model"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0"
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "garage_rpc"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "garage_table"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0"
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "garage_util"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0"
|
||||
|
||||
+17
-147
@@ -1,12 +1,12 @@
|
||||
//! Contains type and functions related to Garage configuration file
|
||||
use std::convert::TryFrom;
|
||||
use std::io::Read;
|
||||
use std::net::SocketAddr;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use serde::{de, Deserialize};
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::socket_address::UnixOrTCPSocketAddress;
|
||||
|
||||
/// Represent the whole configuration
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
@@ -37,11 +37,15 @@ pub struct Config {
|
||||
)]
|
||||
pub compression_level: Option<i32>,
|
||||
|
||||
/// Skip the permission check of secret files. Useful when
|
||||
/// POSIX ACLs (or more complex chmods) are used.
|
||||
#[serde(default)]
|
||||
pub allow_world_readable_secrets: bool,
|
||||
|
||||
/// RPC secret key: 32 bytes hex encoded
|
||||
pub rpc_secret: Option<String>,
|
||||
/// Optional file where RPC secret key is read from
|
||||
pub rpc_secret_file: Option<String>,
|
||||
|
||||
/// Address to bind for RPC
|
||||
pub rpc_bind_addr: SocketAddr,
|
||||
/// Public IP address of this node
|
||||
@@ -102,7 +106,7 @@ pub struct Config {
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct S3ApiConfig {
|
||||
/// Address and port to bind for api serving
|
||||
pub api_bind_addr: Option<SocketAddr>,
|
||||
pub api_bind_addr: Option<UnixOrTCPSocketAddress>,
|
||||
/// S3 region to use
|
||||
pub s3_region: String,
|
||||
/// Suffix to remove from domain name to find bucket. If None,
|
||||
@@ -114,14 +118,14 @@ pub struct S3ApiConfig {
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct K2VApiConfig {
|
||||
/// Address and port to bind for api serving
|
||||
pub api_bind_addr: SocketAddr,
|
||||
pub api_bind_addr: UnixOrTCPSocketAddress,
|
||||
}
|
||||
|
||||
/// Configuration for serving files as normal web server
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct WebConfig {
|
||||
/// Address and port to bind for web serving
|
||||
pub bind_addr: SocketAddr,
|
||||
pub bind_addr: UnixOrTCPSocketAddress,
|
||||
/// Suffix to remove from domain name to find bucket
|
||||
pub root_domain: String,
|
||||
}
|
||||
@@ -130,7 +134,7 @@ pub struct WebConfig {
|
||||
#[derive(Deserialize, Debug, Clone, Default)]
|
||||
pub struct AdminConfig {
|
||||
/// Address and port to bind for admin API serving
|
||||
pub api_bind_addr: Option<SocketAddr>,
|
||||
pub api_bind_addr: Option<UnixOrTCPSocketAddress>,
|
||||
|
||||
/// Bearer token to use to scrape metrics
|
||||
pub metrics_token: Option<String>,
|
||||
@@ -193,6 +197,13 @@ pub struct KubernetesDiscoveryConfig {
|
||||
pub skip_crd: bool,
|
||||
}
|
||||
|
||||
/// Read and parse configuration
|
||||
pub fn read_config(config_file: PathBuf) -> Result<Config, Error> {
|
||||
let config = std::fs::read_to_string(config_file)?;
|
||||
|
||||
Ok(toml::from_str(&config)?)
|
||||
}
|
||||
|
||||
fn default_db_engine() -> String {
|
||||
"sled".into()
|
||||
}
|
||||
@@ -207,68 +218,6 @@ fn default_block_size() -> usize {
|
||||
1048576
|
||||
}
|
||||
|
||||
/// Read and parse configuration
|
||||
pub fn read_config(config_file: PathBuf) -> Result<Config, Error> {
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.open(config_file.as_path())?;
|
||||
|
||||
let mut config = String::new();
|
||||
file.read_to_string(&mut config)?;
|
||||
|
||||
let mut parsed_config: Config = toml::from_str(&config)?;
|
||||
|
||||
secret_from_file(
|
||||
&mut parsed_config.rpc_secret,
|
||||
&parsed_config.rpc_secret_file,
|
||||
"rpc_secret",
|
||||
)?;
|
||||
secret_from_file(
|
||||
&mut parsed_config.admin.metrics_token,
|
||||
&parsed_config.admin.metrics_token_file,
|
||||
"admin.metrics_token",
|
||||
)?;
|
||||
secret_from_file(
|
||||
&mut parsed_config.admin.admin_token,
|
||||
&parsed_config.admin.admin_token_file,
|
||||
"admin.admin_token",
|
||||
)?;
|
||||
|
||||
Ok(parsed_config)
|
||||
}
|
||||
|
||||
fn secret_from_file(
|
||||
secret: &mut Option<String>,
|
||||
secret_file: &Option<String>,
|
||||
name: &'static str,
|
||||
) -> Result<(), Error> {
|
||||
match (&secret, &secret_file) {
|
||||
(_, None) => {
|
||||
// no-op
|
||||
}
|
||||
(Some(_), Some(_)) => {
|
||||
return Err(format!("only one of `{}` and `{}_file` can be set", name, name).into());
|
||||
}
|
||||
(None, Some(file_path)) => {
|
||||
#[cfg(unix)]
|
||||
if std::env::var("GARAGE_ALLOW_WORLD_READABLE_SECRETS").as_deref() != Ok("true") {
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
let metadata = std::fs::metadata(file_path)?;
|
||||
if metadata.mode() & 0o077 != 0 {
|
||||
return Err(format!("File {} is world-readable! (mode: 0{:o}, expected 0600)\nRefusing to start until this is fixed, or environment variable GARAGE_ALLOW_WORLD_READABLE_SECRETS is set to true.", file_path, metadata.mode()).into());
|
||||
}
|
||||
}
|
||||
let mut file = std::fs::OpenOptions::new().read(true).open(file_path)?;
|
||||
let mut secret_buf = String::new();
|
||||
file.read_to_string(&mut secret_buf)?;
|
||||
// trim_end: allows for use case such as `echo "$(openssl rand -hex 32)" > somefile`.
|
||||
// also editors sometimes add a trailing newline
|
||||
*secret = Some(String::from(secret_buf.trim_end()));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn default_compression() -> Option<i32> {
|
||||
Some(1)
|
||||
}
|
||||
@@ -397,83 +346,4 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rpc_secret_file_works() -> Result<(), Error> {
|
||||
let path_secret = mktemp::Temp::new_file()?;
|
||||
let mut file_secret = File::create(path_secret.as_path())?;
|
||||
writeln!(file_secret, "foo")?;
|
||||
drop(file_secret);
|
||||
|
||||
let path_config = mktemp::Temp::new_file()?;
|
||||
let mut file_config = File::create(path_config.as_path())?;
|
||||
let path_secret_path = path_secret.as_path();
|
||||
writeln!(
|
||||
file_config,
|
||||
r#"
|
||||
metadata_dir = "/tmp/garage/meta"
|
||||
data_dir = "/tmp/garage/data"
|
||||
replication_mode = "3"
|
||||
rpc_bind_addr = "[::]:3901"
|
||||
rpc_secret_file = "{}"
|
||||
|
||||
[s3_api]
|
||||
s3_region = "garage"
|
||||
api_bind_addr = "[::]:3900"
|
||||
"#,
|
||||
path_secret_path.display()
|
||||
)?;
|
||||
let config = super::read_config(path_config.to_path_buf())?;
|
||||
assert_eq!("foo", config.rpc_secret.unwrap());
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let metadata = std::fs::metadata(&path_secret_path)?;
|
||||
let mut perm = metadata.permissions();
|
||||
perm.set_mode(0o660);
|
||||
std::fs::set_permissions(&path_secret_path, perm)?;
|
||||
|
||||
std::env::set_var("GARAGE_ALLOW_WORLD_READABLE_SECRETS", "false");
|
||||
assert!(super::read_config(path_config.to_path_buf()).is_err());
|
||||
|
||||
std::env::set_var("GARAGE_ALLOW_WORLD_READABLE_SECRETS", "true");
|
||||
assert!(super::read_config(path_config.to_path_buf()).is_ok());
|
||||
}
|
||||
|
||||
drop(path_config);
|
||||
drop(path_secret);
|
||||
drop(file_config);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rcp_secret_and_rpc_secret_file_cannot_be_set_both() -> Result<(), Error> {
|
||||
let path_config = mktemp::Temp::new_file()?;
|
||||
let mut file_config = File::create(path_config.as_path())?;
|
||||
writeln!(
|
||||
file_config,
|
||||
r#"
|
||||
metadata_dir = "/tmp/garage/meta"
|
||||
data_dir = "/tmp/garage/data"
|
||||
replication_mode = "3"
|
||||
rpc_bind_addr = "[::]:3901"
|
||||
rpc_secret= "dummy"
|
||||
rpc_secret_file = "dummy"
|
||||
|
||||
[s3_api]
|
||||
s3_region = "garage"
|
||||
api_bind_addr = "[::]:3900"
|
||||
"#
|
||||
)?;
|
||||
assert_eq!(
|
||||
"only one of `rpc_secret` and `rpc_secret_file` can be set",
|
||||
super::read_config(path_config.to_path_buf())
|
||||
.unwrap_err()
|
||||
.to_string()
|
||||
);
|
||||
drop(path_config);
|
||||
drop(file_config);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ pub mod forwarded_headers;
|
||||
pub mod metrics;
|
||||
pub mod migrate;
|
||||
pub mod persister;
|
||||
pub mod socket_address;
|
||||
pub mod time;
|
||||
pub mod tranquilizer;
|
||||
pub mod version;
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::net::SocketAddr;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum UnixOrTCPSocketAddress {
|
||||
TCPSocket(SocketAddr),
|
||||
UnixSocket(PathBuf),
|
||||
}
|
||||
|
||||
impl Display for UnixOrTCPSocketAddress {
|
||||
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
UnixOrTCPSocketAddress::TCPSocket(address) => write!(formatter, "http://{}", address),
|
||||
UnixOrTCPSocketAddress::UnixSocket(path) => {
|
||||
write!(formatter, "http+unix://{}", path.to_string_lossy())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for UnixOrTCPSocketAddress {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let string = String::deserialize(deserializer)?;
|
||||
let string = string.as_str();
|
||||
|
||||
if string.starts_with("/") {
|
||||
Ok(UnixOrTCPSocketAddress::UnixSocket(
|
||||
PathBuf::from_str(string).map_err(Error::custom)?,
|
||||
))
|
||||
} else {
|
||||
Ok(UnixOrTCPSocketAddress::TCPSocket(
|
||||
SocketAddr::from_str(string).map_err(Error::custom)?,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "garage_web"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>", "Quentin Dufour <quentin@dufour.io>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0"
|
||||
@@ -27,5 +27,8 @@ futures = "0.3"
|
||||
|
||||
http = "0.2"
|
||||
hyper = { version = "0.14", features = ["server", "http1", "runtime", "tcp", "stream"] }
|
||||
hyperlocal = { version = "0.8.0", default-features = false, features = ["server"] }
|
||||
|
||||
tokio = { version = "1.0", default-features = false, features = ["net"] }
|
||||
|
||||
opentelemetry = "0.17"
|
||||
|
||||
+49
-9
@@ -1,4 +1,6 @@
|
||||
use std::{convert::Infallible, net::SocketAddr, sync::Arc};
|
||||
use std::fs::{self, Permissions};
|
||||
use std::os::unix::prelude::PermissionsExt;
|
||||
use std::{convert::Infallible, sync::Arc};
|
||||
|
||||
use futures::future::Future;
|
||||
|
||||
@@ -9,6 +11,10 @@ use hyper::{
|
||||
Body, Method, Request, Response, Server, StatusCode,
|
||||
};
|
||||
|
||||
use hyperlocal::UnixServerExt;
|
||||
|
||||
use tokio::net::UnixStream;
|
||||
|
||||
use opentelemetry::{
|
||||
global,
|
||||
metrics::{Counter, ValueRecorder},
|
||||
@@ -32,6 +38,7 @@ use garage_util::data::Uuid;
|
||||
use garage_util::error::Error as GarageError;
|
||||
use garage_util::forwarded_headers;
|
||||
use garage_util::metrics::{gen_trace_id, RecordDuration};
|
||||
use garage_util::socket_address::UnixOrTCPSocketAddress;
|
||||
|
||||
struct WebMetrics {
|
||||
request_counter: Counter<u64>,
|
||||
@@ -69,7 +76,7 @@ impl WebServer {
|
||||
/// Run a web server
|
||||
pub async fn run(
|
||||
garage: Arc<Garage>,
|
||||
addr: SocketAddr,
|
||||
addr: UnixOrTCPSocketAddress,
|
||||
root_domain: String,
|
||||
shutdown_signal: impl Future<Output = ()>,
|
||||
) -> Result<(), GarageError> {
|
||||
@@ -80,7 +87,7 @@ impl WebServer {
|
||||
root_domain,
|
||||
});
|
||||
|
||||
let service = make_service_fn(|conn: &AddrStream| {
|
||||
let tcp_service = make_service_fn(|conn: &AddrStream| {
|
||||
let web_server = web_server.clone();
|
||||
|
||||
let client_addr = conn.remote_addr();
|
||||
@@ -88,23 +95,56 @@ impl WebServer {
|
||||
Ok::<_, Error>(service_fn(move |req: Request<Body>| {
|
||||
let web_server = web_server.clone();
|
||||
|
||||
web_server.handle_request(req, client_addr)
|
||||
web_server.handle_request(req, client_addr.to_string())
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
||||
let server = Server::bind(&addr).serve(service);
|
||||
let graceful = server.with_graceful_shutdown(shutdown_signal);
|
||||
info!("Web server listening on http://{}", addr);
|
||||
let unix_service = make_service_fn(|_: &UnixStream| {
|
||||
let web_server = web_server.clone();
|
||||
|
||||
let path = addr.to_string();
|
||||
async move {
|
||||
Ok::<_, Error>(service_fn(move |req: Request<Body>| {
|
||||
let web_server = web_server.clone();
|
||||
|
||||
web_server.handle_request(req, path.clone())
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
||||
info!("Web server listening on {}", addr);
|
||||
|
||||
match addr {
|
||||
UnixOrTCPSocketAddress::TCPSocket(addr) => {
|
||||
Server::bind(&addr)
|
||||
.serve(tcp_service)
|
||||
.with_graceful_shutdown(shutdown_signal)
|
||||
.await?
|
||||
}
|
||||
UnixOrTCPSocketAddress::UnixSocket(ref path) => {
|
||||
if path.exists() {
|
||||
fs::remove_file(path)?
|
||||
}
|
||||
|
||||
let bound = Server::bind_unix(path)?;
|
||||
|
||||
fs::set_permissions(path, Permissions::from_mode(0o222))?;
|
||||
|
||||
bound
|
||||
.serve(unix_service)
|
||||
.with_graceful_shutdown(shutdown_signal)
|
||||
.await?;
|
||||
}
|
||||
};
|
||||
|
||||
graceful.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_request(
|
||||
self: Arc<Self>,
|
||||
req: Request<Body>,
|
||||
addr: SocketAddr,
|
||||
addr: String,
|
||||
) -> Result<Response<Body>, Infallible> {
|
||||
if let Ok(forwarded_for_ip_addr) =
|
||||
forwarded_headers::handle_forwarded_for_headers(req.headers())
|
||||
|
||||
Reference in New Issue
Block a user