Compare commits

..

15 Commits
api-v1 ... main

Author SHA1 Message Date
2919906843 Clean up nix setup, only toolchain.nix is still in a weird state 2023-12-11 17:46:18 +01:00
a34cec60d4 windows and linux builds fully working 2023-12-10 17:48:12 +01:00
e46bcfda3f Windows pure shell build works 2023-12-10 16:17:06 +01:00
f6b1f1fc23 Fixed nix shell for local build 2023-12-10 15:00:43 +01:00
7008e1653b Got windows build working within a shell, with no features enabled 2023-12-10 14:06:36 +01:00
bd09a1ad7b Code changes for windows, mostly to get rid of unix socket listening because tokio doesn't support it 2023-12-10 10:45:48 +01:00
b84a60ba69 Fixed libsodium issue for windows build 2023-12-05 15:05:10 +01:00
25f55cf24d use fenix/naersk for nix building, got local x86_64-linux build working 2023-12-03 22:16:36 +01:00
Alex
a8b0e01f88 Merge pull request 'OpenAPI specification of admin APIv1' (#672) from api-v1 into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/672
2023-11-29 15:42:46 +00:00
Alex
ffa659433d Merge pull request 'Doc: fix db_engines section and improve config reference' (#674) from fix-doc-db-engine into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/674
2023-11-28 12:03:46 +00:00
Alex Auvolat
cfa5550cb2 doc: move replication_mode to top of configuration page reference 2023-11-28 11:58:27 +01:00
Alex Auvolat
939d1f2e17 doc: improve navigation in configuration reference 2023-11-28 11:53:26 +01:00
Alex Auvolat
1f6efe57be doc: update the db_engine section 2023-11-28 11:33:31 +01:00
Alex
36bd21a148 Merge pull request 'Allow 0 as a part number marker' (#670) from asonix/garage:main into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/670
2023-11-22 10:33:31 +00:00
asonix
92fd899fb6 Allow 0 as a part number marker 2023-11-21 17:39:51 -06:00
22 changed files with 529 additions and 6818 deletions

View File

@ -1,3 +0,0 @@
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=mold"]

2
.gitignore vendored
View File

@ -4,3 +4,5 @@
**/*.rs.bk **/*.rs.bk
*.swp *.swp
/.direnv /.direnv
/.cargo
/result

15
Cargo.lock generated
View File

@ -1271,7 +1271,6 @@ dependencies = [
"http-range", "http-range",
"httpdate", "httpdate",
"hyper", "hyper",
"hyperlocal",
"idna", "idna",
"md-5", "md-5",
"multer", "multer",
@ -1465,7 +1464,6 @@ dependencies = [
"garage_util", "garage_util",
"http", "http",
"hyper", "hyper",
"hyperlocal",
"opentelemetry", "opentelemetry",
"percent-encoding", "percent-encoding",
"tokio", "tokio",
@ -1778,19 +1776,6 @@ dependencies = [
"tokio-io-timeout", "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]] [[package]]
name = "iana-time-zone" name = "iana-time-zone"
version = "0.1.57" version = "0.1.57"

6326
Cargo.nix

File diff suppressed because it is too large Load Diff

View File

@ -1,56 +1,166 @@
{ system ? builtins.currentSystem, git_version ? null, }: {
buildSystem ? builtins.currentSystem,
with import ./nix/common.nix; targetSystem ? buildSystem,
gitVersion ? null,
release ? false,
features ? null,
}:
let let
pkgs = import pkgsSrc { }; pkgsSrc = import ./nix/pkgs.nix;
compile = import ./nix/compile.nix; newBuildTarget = {
nixPkgsSystem,
build_debug_and_release = (target: { rustTarget ? nixPkgsSystem,
debug = (compile { nativeBuildInputs ? pkgsCross: [],
inherit system target git_version pkgsSrc cargo2nixOverlay; rustFlags ? pkgsCross: [],
release = false; }: {
}).workspace.garage { compileMode = "build"; }; inherit nixPkgsSystem rustTarget nativeBuildInputs rustFlags;
release = (compile {
inherit system target git_version pkgsSrc cargo2nixOverlay;
release = true;
}).workspace.garage { compileMode = "build"; };
});
test = (rustPkgs:
pkgs.symlinkJoin {
name = "garage-tests";
paths =
builtins.map (key: rustPkgs.workspace.${key} { compileMode = "test"; })
(builtins.attrNames rustPkgs.workspace);
});
in {
pkgs = {
amd64 = build_debug_and_release "x86_64-unknown-linux-musl";
i386 = build_debug_and_release "i686-unknown-linux-musl";
arm64 = build_debug_and_release "aarch64-unknown-linux-musl";
arm = build_debug_and_release "armv6l-unknown-linux-musleabihf";
}; };
test = {
amd64 = test (compile { # centralize per-target configuration in a single place.
inherit system git_version pkgsSrc cargo2nixOverlay; buildTargets = {
target = "x86_64-unknown-linux-musl"; "x86_64-linux" = newBuildTarget {
features = [ nixPkgsSystem = "x86_64-unknown-linux-musl";
"garage/bundled-libs" };
"garage/k2v"
"garage/sled" "i686-linux" = newBuildTarget {
"garage/lmdb" nixPkgsSystem = "i686-unknown-linux-musl";
"garage/sqlite" };
"aarch64-linux" = newBuildTarget {
nixPkgsSystem = "aarch64-unknown-linux-musl";
};
# Old Raspberry Pi's (not currently supported due to linking errors with
# libsqlite3 and libsodium
#"armv6l-linux" = newBuildTarget {
# nixPkgsSystem = "armv6l-unknown-linux-musleabihf";
# rustTarget = "arm-unknown-linux-musleabihf";
#};
"x86_64-windows" = newBuildTarget {
nixPkgsSystem = "x86_64-w64-mingw32";
rustTarget = "x86_64-pc-windows-gnu";
nativeBuildInputs = pkgsCross: [ pkgsCross.windows.pthreads ];
rustFlags = pkgsCross: [
"-C" "link-arg=-L${pkgsCross.windows.pthreads}/lib"
]; ];
});
}; };
clippy = {
amd64 = (compile {
inherit system git_version pkgsSrc cargo2nixOverlay;
target = "x86_64-unknown-linux-musl";
compiler = "clippy";
}).workspace.garage { compileMode = "build"; };
}; };
buildTarget = buildTargets.${targetSystem};
pkgs = import pkgsSrc { system = buildSystem; };
pkgsCross = import pkgsSrc {
system = buildSystem;
crossSystem.config = buildTarget.nixPkgsSystem;
};
rustTarget = buildTarget.rustTarget;
toolchain = let
fenix = import (pkgs.fetchFromGitHub {
owner = "nix-community";
repo = "fenix";
rev = "81ab0b4f7ae9ebb57daa0edf119c4891806e4d3a";
hash = "sha256-bZmI7ytPAYLpyFNgj5xirDkKuAniOkj1xHdv5aIJ5GM=";
}) {
system = buildSystem;
};
mkToolchain = fenixTarget: fenixTarget.toolchainOf {
channel = "1.68.2";
sha256 = "sha256-4vetmUhTUsew5FODnjlnQYInzyLNyDwocGa4IvMk3DM=";
};
in
fenix.combine [
(mkToolchain fenix).rustc
(mkToolchain fenix).rustfmt
(mkToolchain fenix).cargo
(mkToolchain fenix).clippy
(mkToolchain fenix.targets.${rustTarget}).rust-std
];
naersk = let
naerskSrc = pkgs.fetchFromGitHub {
owner = "nix-community";
repo = "naersk";
rev = "d9a33d69a9c421d64c8d925428864e93be895dcc";
hash = "sha256-e136hTT7LqQ2QjOTZQMW+jnsevWwBpMj78u6FRUsH9I=";
};
in
pkgs.callPackages naerskSrc {
cargo = toolchain;
rustc = toolchain;
};
builtFeatures = if features != null then
features
else (
[ "garage/bundled-libs" "garage/sled" "garage/lmdb" "garage/k2v" ] ++ (
if release then [
"garage/consul-discovery"
"garage/kubernetes-discovery"
"garage/metrics"
"garage/telemetry-otlp"
"garage/sqlite"
] else [ ]
)
);
# For some reason the pkgsCross.pkgsStatic build of libsodium doesn't contain
# a `.a` file when compiled to a windows target, but rather contains
# a `.dll.a` file which libsodium-sys doesn't pick up on. Copying the one to
# the be the other seems to work.
libsodium = pkgs.runCommand "libsodium-wrapped" {
libsodium = pkgsCross.pkgsStatic.libsodium;
} ''
cp -rL "$libsodium" "$out"
chmod -R +w "$out"
if [ ! -e "$out"/lib/libsodium.a ] && [ -f "$out"/lib/libsodium.dll.a ]; then
cp "$out"/lib/libsodium.dll.a "$out"/lib/libsodium.a
fi
'';
in rec {
inherit pkgs pkgsCross;
# Exported separately so it can be used from shell.nix
buildEnv = rec {
nativeBuildInputs = (buildTarget.nativeBuildInputs pkgsCross) ++ [
toolchain
pkgs.protobuf
# Required for shell because of rust dependency build scripts which must
# run on the build system.
pkgs.stdenv.cc
];
SODIUM_LIB_DIR = "${libsodium}/lib";
# Required because ring crate is special. This also seems to have
# fixed some issues with the x86_64-windows cross-compile :shrug:
TARGET_CC = "${pkgsCross.stdenv.cc}/bin/${pkgsCross.stdenv.cc.targetPrefix}cc";
CARGO_BUILD_TARGET = rustTarget;
CARGO_BUILD_RUSTFLAGS = [
"-C" "target-feature=+crt-static"
"-C" "link-arg=-static"
# https://github.com/rust-lang/cargo/issues/4133
"-C" "linker=${TARGET_CC}"
] ++ (buildTarget.rustFlags pkgsCross);
};
build = naersk.buildPackage (rec {
inherit release;
src = ./.;
strictDeps = true;
doCheck = false;
cargoBuildOptions = prev: prev++[
"--features=${builtins.concatStringsSep "," builtFeatures}"
];
} // buildEnv);
} }

View File

@ -38,7 +38,7 @@ Our website serving logic is as follow:
Now we need to infer the URL of your website through your bucket name. Now we need to infer the URL of your website through your bucket name.
Let assume: Let assume:
- we set `root_domain = ".web.example.com"` in `garage.toml` ([ref](@/documentation/reference-manual/configuration.md#root_domain)) - we set `root_domain = ".web.example.com"` in `garage.toml` ([ref](@/documentation/reference-manual/configuration.md#web_root_domain))
- our bucket name is `garagehq.deuxfleurs.fr`. - our bucket name is `garagehq.deuxfleurs.fr`.
Our bucket will be served if the Host field matches one of these 2 values (the port is ignored): Our bucket will be served if the Host field matches one of these 2 values (the port is ignored):

View File

@ -12,7 +12,7 @@ An introduction to building cluster layouts can be found in the [production depl
In Garage, all of the data that can be stored in a given cluster is divided In Garage, all of the data that can be stored in a given cluster is divided
into slices which we call *partitions*. Each partition is stored by into slices which we call *partitions*. Each partition is stored by
one or several nodes in the cluster one or several nodes in the cluster
(see [`replication_mode`](@/documentation/reference-manual/configuration.md#replication-mode)). (see [`replication_mode`](@/documentation/reference-manual/configuration.md#replication_mode)).
The layout determines the correspondence between these partition, The layout determines the correspondence between these partition,
which exist on a logical level, and actual storage nodes. which exist on a logical level, and actual storage nodes.

View File

@ -8,6 +8,8 @@ weight = 20
Here is an example `garage.toml` configuration file that illustrates all of the possible options: Here is an example `garage.toml` configuration file that illustrates all of the possible options:
```toml ```toml
replication_mode = "3"
metadata_dir = "/var/lib/garage/meta" metadata_dir = "/var/lib/garage/meta"
data_dir = "/var/lib/garage/data" data_dir = "/var/lib/garage/data"
metadata_fsync = true metadata_fsync = true
@ -21,8 +23,6 @@ sled_cache_capacity = "128MiB"
sled_flush_every_ms = 2000 sled_flush_every_ms = 2000
lmdb_map_size = "1T" lmdb_map_size = "1T"
replication_mode = "3"
compression_level = 1 compression_level = 1
rpc_secret = "4425f5c26c5e11581d3223904324dcb5b5d5dfb14e5e7f35e38c595424f5f1e6" rpc_secret = "4425f5c26c5e11581d3223904324dcb5b5d5dfb14e5e7f35e38c595424f5f1e6"
@ -77,157 +77,64 @@ The following gives details about each available configuration option.
## Available configuration options ## Available configuration options
### `metadata_dir` ### Index
The directory in which Garage will store its metadata. This contains the node identifier, Top-level configuration options:
the network configuration and the peer list, the list of buckets and keys as well [`block_size`](#block_size),
as the index of all objects, object version and object blocks. [`bootstrap_peers`](#bootstrap_peers),
[`compression_level`](#compression_level),
[`data_dir`](#metadata_dir),
[`data_fsync`](#data_fsync),
[`db_engine`](#db_engine),
[`lmdb_map_size`](#lmdb_map_size),
[`metadata_dir`](#metadata_dir),
[`metadata_fsync`](#metadata_fsync),
[`replication_mode`](#replication_mode),
[`rpc_bind_addr`](#rpc_bind_addr),
[`rpc_public_addr`](#rpc_public_addr),
[`rpc_secret`](#rpc_secret),
[`rpc_secret_file`](#rpc_secret),
[`sled_cache_capacity`](#sled_cache_capacity),
[`sled_flush_every_ms`](#sled_flush_every_ms).
Store this folder on a fast SSD drive if possible to maximize Garage's performance. The `[consul_discovery]` section:
[`api`](#consul_api),
[`ca_cert`](#consul_ca_cert),
[`client_cert`](#consul_client_cert),
[`client_key`](#consul_client_cert),
[`consul_http_addr`](#consul_http_addr),
[`meta`](#consul_tags),
[`service_name`](#consul_service_name),
[`tags`](#consul_tags),
[`tls_skip_verify`](#consul_tls_skip_verify),
[`token`](#consul_token).
### `data_dir` The `[kubernetes_discovery]` section:
[`namespace`](#kube_namespace),
[`service_name`](#kube_service_name),
[`skip_crd`](#kube_skip_crd).
The directory in which Garage will store the data blocks of objects. The `[s3_api]` section:
This folder can be placed on an HDD. The space available for `data_dir` [`api_bind_addr`](#s3_api_bind_addr),
should be counted to determine a node's capacity [`root_domain`](#s3_root_domain),
when [adding it to the cluster layout](@/documentation/cookbook/real-world.md). [`s3_region`](#s3_region).
Since `v0.9.0`, Garage supports multiple data directories with the following syntax: The `[s3_web]` section:
[`bind_addr`](#web_bind_addr),
[`root_domain`](#web_root_domain).
```toml The `[admin]` section:
data_dir = [ [`api_bind_addr`](#admin_api_bind_addr),
{ path = "/path/to/old_data", read_only = true }, [`metrics_token`](#admin_metrics_token),
{ path = "/path/to/new_hdd1", capacity = "2T" }, [`metrics_token_file`](#admin_metrics_token),
{ path = "/path/to/new_hdd2", capacity = "4T" }, [`admin_token`](#admin_token),
] [`admin_token_file`](#admin_token),
``` [`trace_sink`](#admin_trace_sink),
See [the dedicated documentation page](@/documentation/operations/multi-hdd.md)
on how to operate Garage in such a setup.
### `db_engine` (since `v0.8.0`) ### Top-level configuration options
By default, Garage uses the Sled embedded database library #### `replication_mode` {#replication_mode}
to store its metadata on-disk. Since `v0.8.0`, Garage can use alternative storage backends as follows:
| DB engine | `db_engine` value | Database path |
| --------- | ----------------- | ------------- |
| [Sled](https://sled.rs) | `"sled"` | `<metadata_dir>/db/` |
| [LMDB](https://www.lmdb.tech) | `"lmdb"` | `<metadata_dir>/db.lmdb/` |
| [Sqlite](https://sqlite.org) | `"sqlite"` | `<metadata_dir>/db.sqlite` |
Performance characteristics of the different DB engines are as follows:
- Sled: the default database engine, which tends to produce
large data files and also has performance issues, especially when the metadata folder
is on a traditional HDD and not on SSD.
- LMDB: the recommended alternative on 64-bit systems,
much more space-efficiant and slightly faster. Note that the data format of LMDB is not portable
between architectures, so for instance the Garage database of an x86-64
node cannot be moved to an ARM64 node. Also note that, while LMDB can technically be used on 32-bit systems,
this will limit your node to very small database sizes due to how LMDB works; it is therefore not recommended.
- Sqlite: Garage supports Sqlite as a storage backend for metadata,
however it may have issues and is also very slow in its current implementation,
so it is not recommended to be used for now.
It is possible to convert Garage's metadata directory from one format to another with a small utility named `convert_db`,
which can be downloaded at the following locations:
[for amd64](https://garagehq.deuxfleurs.fr/_releases/convert_db/amd64/convert_db),
[for i386](https://garagehq.deuxfleurs.fr/_releases/convert_db/i386/convert_db),
[for arm64](https://garagehq.deuxfleurs.fr/_releases/convert_db/arm64/convert_db),
[for arm](https://garagehq.deuxfleurs.fr/_releases/convert_db/arm/convert_db).
The `convert_db` utility is used as folows:
```
convert-db -a <input db engine> -i <input db path> \
-b <output db engine> -o <output db path>
```
Make sure to specify the full database path as presented in the table above,
and not just the path to the metadata directory.
### `metadata_fsync`
Whether to enable synchronous mode for the database engine or not.
This is disabled (`false`) by default.
This reduces the risk of metadata corruption in case of power failures,
at the cost of a significant drop in write performance,
as Garage will have to pause to sync data to disk much more often
(several times for API calls such as PutObject).
Using this option reduces the risk of simultaneous metadata corruption on several
cluster nodes, which could lead to data loss.
If multi-site replication is used, this option is most likely not necessary, as
it is extremely unlikely that two nodes in different locations will have a
power failure at the exact same time.
(Metadata corruption on a single node is not an issue, the corrupted data file
can always be deleted and reconstructed from the other nodes in the cluster.)
Here is how this option impacts the different database engines:
| Database | `metadata_fsync = false` (default) | `metadata_fsync = true` |
|----------|------------------------------------|-------------------------------|
| Sled | default options | *unsupported* |
| Sqlite | `PRAGMA synchronous = OFF` | `PRAGMA synchronous = NORMAL` |
| LMDB | `MDB_NOMETASYNC` + `MDB_NOSYNC` | `MDB_NOMETASYNC` |
Note that the Sqlite database is always ran in `WAL` mode (`PRAGMA journal_mode = WAL`).
### `data_fsync`
Whether to `fsync` data blocks and their containing directory after they are
saved to disk.
This is disabled (`false`) by default.
This might reduce the risk that a data block is lost in rare
situations such as simultaneous node losing power,
at the cost of a moderate drop in write performance.
Similarly to `metatada_fsync`, this is likely not necessary
if geographical replication is used.
### `block_size`
Garage splits stored objects in consecutive chunks of size `block_size`
(except the last one which might be smaller). The default size is 1MiB and
should work in most cases. We recommend increasing it to e.g. 10MiB if
you are using Garage to store large files and have fast network connections
between all nodes (e.g. 1gbps).
If you are interested in tuning this, feel free to do so (and remember to
report your findings to us!). When this value is changed for a running Garage
installation, only files newly uploaded will be affected. Previously uploaded
files will remain available. This however means that chunks from existing files
will not be deduplicated with chunks from newly uploaded files, meaning you
might use more storage space that is optimally possible.
### `sled_cache_capacity`
This parameter can be used to tune the capacity of the cache used by
[sled](https://sled.rs), the database Garage uses internally to store metadata.
Tune this to fit the RAM you wish to make available to your Garage instance.
This value has a conservative default (128MB) so that Garage doesn't use too much
RAM by default, but feel free to increase this for higher performance.
### `sled_flush_every_ms`
This parameters can be used to tune the flushing interval of sled.
Increase this if sled is thrashing your SSD, at the risk of losing more data in case
of a power outage (though this should not matter much as data is replicated on other
nodes). The default value, 2000ms, should be appropriate for most use cases.
### `lmdb_map_size`
This parameters can be used to set the map size used by LMDB,
which is the size of the virtual memory region used for mapping the database file.
The value of this parameter is the maximum size the metadata database can take.
This value is not bound by the physical RAM size of the machine running Garage.
If not specified, it defaults to 1GiB on 32-bit machines and 1TiB on 64-bit machines.
### `replication_mode`
Garage supports the following replication modes: Garage supports the following replication modes:
@ -310,7 +217,160 @@ to the cluster while rebalancing is in progress. In theory, no data should be
lost as rebalancing is a routine operation for Garage, although we cannot lost as rebalancing is a routine operation for Garage, although we cannot
guarantee you that everything will go right in such an extreme scenario. guarantee you that everything will go right in such an extreme scenario.
### `compression_level` #### `metadata_dir` {#metadata_dir}
The directory in which Garage will store its metadata. This contains the node identifier,
the network configuration and the peer list, the list of buckets and keys as well
as the index of all objects, object version and object blocks.
Store this folder on a fast SSD drive if possible to maximize Garage's performance.
#### `data_dir` {#data_dir}
The directory in which Garage will store the data blocks of objects.
This folder can be placed on an HDD. The space available for `data_dir`
should be counted to determine a node's capacity
when [adding it to the cluster layout](@/documentation/cookbook/real-world.md).
Since `v0.9.0`, Garage supports multiple data directories with the following syntax:
```toml
data_dir = [
{ path = "/path/to/old_data", read_only = true },
{ path = "/path/to/new_hdd1", capacity = "2T" },
{ path = "/path/to/new_hdd2", capacity = "4T" },
]
```
See [the dedicated documentation page](@/documentation/operations/multi-hdd.md)
on how to operate Garage in such a setup.
#### `db_engine` (since `v0.8.0`) {#db_engine}
Since `v0.8.0`, Garage can use alternative storage backends as follows:
| DB engine | `db_engine` value | Database path |
| --------- | ----------------- | ------------- |
| [LMDB](https://www.lmdb.tech) (default since `v0.9.0`) | `"lmdb"` | `<metadata_dir>/db.lmdb/` |
| [Sled](https://sled.rs) (default up to `v0.8.0`) | `"sled"` | `<metadata_dir>/db/` |
| [Sqlite](https://sqlite.org) | `"sqlite"` | `<metadata_dir>/db.sqlite` |
Sled was the only database engine up to Garage v0.7.0. Performance issues and
API limitations of Sled prompted the addition of alternative engines in v0.8.0.
Since v0.9.0, LMDB is the default engine instead of Sled, and Sled is
deprecated. We plan to remove Sled in Garage v1.0.
Performance characteristics of the different DB engines are as follows:
- Sled: tends to produce large data files and also has performance issues,
especially when the metadata folder is on a traditional HDD and not on SSD.
- LMDB: the recommended database engine on 64-bit systems, much more
space-efficient and slightly faster. Note that the data format of LMDB is not
portable between architectures, so for instance the Garage database of an
x86-64 node cannot be moved to an ARM64 node. Also note that, while LMDB can
technically be used on 32-bit systems, this will limit your node to very
small database sizes due to how LMDB works; it is therefore not recommended.
- Sqlite: Garage supports Sqlite as an alternative storage backend for
metadata, and although it has not been tested as much, it is expected to work
satisfactorily. Since Garage v0.9.0, performance issues have largely been
fixed by allowing for a no-fsync mode (see `metadata_fsync`). Sqlite does not
have the database size limitation of LMDB on 32-bit systems.
It is possible to convert Garage's metadata directory from one format to another
using the `garage convert-db` command, which should be used as follows:
```
garage convert-db -a <input db engine> -i <input db path> \
-b <output db engine> -o <output db path>
```
Make sure to specify the full database path as presented in the table above
(third colummn), and not just the path to the metadata directory.
#### `metadata_fsync` {#metadata_fsync}
Whether to enable synchronous mode for the database engine or not.
This is disabled (`false`) by default.
This reduces the risk of metadata corruption in case of power failures,
at the cost of a significant drop in write performance,
as Garage will have to pause to sync data to disk much more often
(several times for API calls such as PutObject).
Using this option reduces the risk of simultaneous metadata corruption on several
cluster nodes, which could lead to data loss.
If multi-site replication is used, this option is most likely not necessary, as
it is extremely unlikely that two nodes in different locations will have a
power failure at the exact same time.
(Metadata corruption on a single node is not an issue, the corrupted data file
can always be deleted and reconstructed from the other nodes in the cluster.)
Here is how this option impacts the different database engines:
| Database | `metadata_fsync = false` (default) | `metadata_fsync = true` |
|----------|------------------------------------|-------------------------------|
| Sled | default options | *unsupported* |
| Sqlite | `PRAGMA synchronous = OFF` | `PRAGMA synchronous = NORMAL` |
| LMDB | `MDB_NOMETASYNC` + `MDB_NOSYNC` | `MDB_NOMETASYNC` |
Note that the Sqlite database is always ran in `WAL` mode (`PRAGMA journal_mode = WAL`).
#### `data_fsync` {#data_fsync}
Whether to `fsync` data blocks and their containing directory after they are
saved to disk.
This is disabled (`false`) by default.
This might reduce the risk that a data block is lost in rare
situations such as simultaneous node losing power,
at the cost of a moderate drop in write performance.
Similarly to `metatada_fsync`, this is likely not necessary
if geographical replication is used.
#### `block_size` {#block_size}
Garage splits stored objects in consecutive chunks of size `block_size`
(except the last one which might be smaller). The default size is 1MiB and
should work in most cases. We recommend increasing it to e.g. 10MiB if
you are using Garage to store large files and have fast network connections
between all nodes (e.g. 1gbps).
If you are interested in tuning this, feel free to do so (and remember to
report your findings to us!). When this value is changed for a running Garage
installation, only files newly uploaded will be affected. Previously uploaded
files will remain available. This however means that chunks from existing files
will not be deduplicated with chunks from newly uploaded files, meaning you
might use more storage space that is optimally possible.
#### `sled_cache_capacity` {#sled_cache_capacity}
This parameter can be used to tune the capacity of the cache used by
[sled](https://sled.rs), the database Garage uses internally to store metadata.
Tune this to fit the RAM you wish to make available to your Garage instance.
This value has a conservative default (128MB) so that Garage doesn't use too much
RAM by default, but feel free to increase this for higher performance.
#### `sled_flush_every_ms` {#sled_flush_every_ms}
This parameters can be used to tune the flushing interval of sled.
Increase this if sled is thrashing your SSD, at the risk of losing more data in case
of a power outage (though this should not matter much as data is replicated on other
nodes). The default value, 2000ms, should be appropriate for most use cases.
#### `lmdb_map_size` {#lmdb_map_size}
This parameters can be used to set the map size used by LMDB,
which is the size of the virtual memory region used for mapping the database file.
The value of this parameter is the maximum size the metadata database can take.
This value is not bound by the physical RAM size of the machine running Garage.
If not specified, it defaults to 1GiB on 32-bit machines and 1TiB on 64-bit machines.
#### `compression_level` {#compression_level}
Zstd compression level to use for storing blocks. Zstd compression level to use for storing blocks.
@ -334,7 +394,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 This value can be different between nodes, compression is done by the node which receive the
API call. API call.
### `rpc_secret`, `rpc_secret_file` or `GARAGE_RPC_SECRET` (env) #### `rpc_secret`, `rpc_secret_file` or `GARAGE_RPC_SECRET` (env) {#rpc_secret}
Garage uses a secret key, called an RPC secret, that is shared between all 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 nodes of the cluster in order to identify these nodes and allow them to
@ -346,7 +406,7 @@ 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 given in the configuration variable `rpc_secret_file`, or specified as an
environment variable `GARAGE_RPC_SECRET`. environment variable `GARAGE_RPC_SECRET`.
### `rpc_bind_addr` #### `rpc_bind_addr` {#rpc_bind_addr}
The address and port on which to bind for inter-cluster communcations The address and port on which to bind for inter-cluster communcations
(reffered to as RPC for remote procedure calls). (reffered to as RPC for remote procedure calls).
@ -355,14 +415,14 @@ the node, even in the case of a NAT: the NAT should be configured to forward the
port number to the same internal port nubmer. This means that if you have several nodes running port number to the same internal port nubmer. This means that if you have several nodes running
behind a NAT, they should each use a different RPC port number. behind a NAT, they should each use a different RPC port number.
### `rpc_public_addr` #### `rpc_public_addr` {#rpc_public_addr}
The address and port that other nodes need to use to contact this node for The address and port that other nodes need to use to contact this node for
RPC calls. **This parameter is optional but recommended.** In case you have RPC calls. **This parameter is optional but recommended.** In case you have
a NAT that binds the RPC port to a port that is different on your public IP, a NAT that binds the RPC port to a port that is different on your public IP,
this field might help making it work. this field might help making it work.
### `bootstrap_peers` #### `bootstrap_peers` {#bootstrap_peers}
A list of peer identifiers on which to contact other Garage peers of this cluster. A list of peer identifiers on which to contact other Garage peers of this cluster.
These peer identifiers have the following syntax: These peer identifiers have the following syntax:
@ -379,42 +439,42 @@ key will be returned by `garage node id` and you will have to add the IP
yourself. yourself.
## The `[consul_discovery]` section ### The `[consul_discovery]` section
Garage supports discovering other nodes of the cluster using Consul. For this Garage supports discovering other nodes of the cluster using Consul. For this
to work correctly, nodes need to know their IP address by which they can be to work correctly, nodes need to know their IP address by which they can be
reached by other nodes of the cluster, which should be set in `rpc_public_addr`. reached by other nodes of the cluster, which should be set in `rpc_public_addr`.
### `consul_http_addr` and `service_name` #### `consul_http_addr` {#consul_http_addr}
The `consul_http_addr` parameter should be set to the full HTTP(S) address of the Consul server. The `consul_http_addr` parameter should be set to the full HTTP(S) address of the Consul server.
### `api` #### `api` {#consul_api}
Two APIs for service registration are supported: `catalog` and `agent`. `catalog`, the default, will register a service using Two APIs for service registration are supported: `catalog` and `agent`. `catalog`, the default, will register a service using
the `/v1/catalog` endpoints, enabling mTLS if `client_cert` and `client_key` are provided. The `agent` API uses the the `/v1/catalog` endpoints, enabling mTLS if `client_cert` and `client_key` are provided. The `agent` API uses the
`v1/agent` endpoints instead, where an optional `token` may be provided. `v1/agent` endpoints instead, where an optional `token` may be provided.
### `service_name` #### `service_name` {#consul_service_name}
`service_name` should be set to the service name under which Garage's `service_name` should be set to the service name under which Garage's
RPC ports are announced. RPC ports are announced.
### `client_cert`, `client_key` #### `client_cert`, `client_key` {#consul_client_cert}
TLS client certificate and client key to use when communicating with Consul over TLS. Both are mandatory when doing so. TLS client certificate and client key to use when communicating with Consul over TLS. Both are mandatory when doing so.
Only available when `api = "catalog"`. Only available when `api = "catalog"`.
### `ca_cert` #### `ca_cert` {#consul_ca_cert}
TLS CA certificate to use when communicating with Consul over TLS. TLS CA certificate to use when communicating with Consul over TLS.
### `tls_skip_verify` #### `tls_skip_verify` {#consul_tls_skip_verify}
Skip server hostname verification in TLS handshake. Skip server hostname verification in TLS handshake.
`ca_cert` is ignored when this is set. `ca_cert` is ignored when this is set.
### `token` #### `token` {#consul_token}
Uses the provided token for communication with Consul. Only available when `api = "agent"`. Uses the provided token for communication with Consul. Only available when `api = "agent"`.
The policy assigned to this token should at least have these rules: The policy assigned to this token should at least have these rules:
@ -434,49 +494,49 @@ node_prefix "" {
} }
``` ```
### `tags` and `meta` #### `tags` and `meta` {#consul_tags}
Additional list of tags and map of service meta to add during service registration. Additional list of tags and map of service meta to add during service registration.
## The `[kubernetes_discovery]` section ### The `[kubernetes_discovery]` section
Garage supports discovering other nodes of the cluster using kubernetes custom Garage supports discovering other nodes of the cluster using kubernetes custom
resources. For this to work, a `[kubernetes_discovery]` section must be present resources. For this to work, a `[kubernetes_discovery]` section must be present
with at least the `namespace` and `service_name` parameters. with at least the `namespace` and `service_name` parameters.
### `namespace` #### `namespace` {#kube_namespace}
`namespace` sets the namespace in which the custom resources are `namespace` sets the namespace in which the custom resources are
configured. configured.
### `service_name` #### `service_name` {#kube_service_name}
`service_name` is added as a label to the advertised resources to `service_name` is added as a label to the advertised resources to
filter them, to allow for multiple deployments in a single namespace. filter them, to allow for multiple deployments in a single namespace.
### `skip_crd` #### `skip_crd` {#kube_skip_crd}
`skip_crd` can be set to true to disable the automatic creation and `skip_crd` can be set to true to disable the automatic creation and
patching of the `garagenodes.deuxfleurs.fr` CRD. You will need to create the CRD patching of the `garagenodes.deuxfleurs.fr` CRD. You will need to create the CRD
manually. manually.
## The `[s3_api]` section ### The `[s3_api]` section
### `api_bind_addr` #### `api_bind_addr` {#s3_api_bind_addr}
The IP and port on which to bind for accepting S3 API calls. 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. 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. Alternatively, since `v0.8.5`, a path can be used to create a unix socket with 0222 mode.
### `s3_region` #### `s3_region` {#s3_region}
Garage will accept S3 API calls that are targetted to the S3 region defined here. Garage will accept S3 API calls that are targetted to the S3 region defined here.
API calls targetted to other regions will fail with a AuthorizationHeaderMalformed error API calls targetted to other regions will fail with a AuthorizationHeaderMalformed error
message that redirects the client to the correct region. message that redirects the client to the correct region.
### `root_domain` {#root_domain} #### `root_domain` {#s3_root_domain}
The optional suffix to access bucket using vhost-style in addition to path-style request. The optional suffix to access bucket using vhost-style in addition to path-style request.
Note path-style requests are always enabled, whether or not vhost-style is configured. Note path-style requests are always enabled, whether or not vhost-style is configured.
@ -488,12 +548,12 @@ using the hostname `my-bucket.s3.garage.eu`.
## The `[s3_web]` section ### The `[s3_web]` section
Garage allows to publish content of buckets as websites. This section configures the Garage allows to publish content of buckets as websites. This section configures the
behaviour of this module. behaviour of this module.
### `bind_addr` #### `bind_addr` {#web_bind_addr}
The IP and port on which to bind for accepting HTTP requests to buckets configured The IP and port on which to bind for accepting HTTP requests to buckets configured
for website access. for website access.
@ -501,7 +561,7 @@ 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. Alternatively, since `v0.8.5`, a path can be used to create a unix socket with 0222 mode.
### `root_domain` #### `root_domain` {#web_root_domain}
The optional suffix appended to bucket names for the corresponding HTTP Host. The optional suffix appended to bucket names for the corresponding HTTP Host.
@ -510,11 +570,11 @@ will be accessible either with hostname `deuxfleurs.fr.web.garage.eu`
or with hostname `deuxfleurs.fr`. or with hostname `deuxfleurs.fr`.
## The `[admin]` section ### The `[admin]` section
Garage has a few administration capabilities, in particular to allow remote monitoring. These features are detailed below. Garage has a few administration capabilities, in particular to allow remote monitoring. These features are detailed below.
### `api_bind_addr` #### `api_bind_addr` {#admin_api_bind_addr}
If specified, Garage will bind an HTTP server to this port and address, on If specified, Garage will bind an HTTP server to this port and address, on
which it will listen to requests for administration features. which it will listen to requests for administration features.
@ -523,7 +583,7 @@ See [administration API reference](@/documentation/reference-manual/admin-api.md
Alternatively, since `v0.8.5`, a path can be used to create a unix socket. Note that for security reasons, 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. the socket will have 0220 mode. Make sure to set user and group permissions accordingly.
### `metrics_token`, `metrics_token_file` or `GARAGE_METRICS_TOKEN` (env) #### `metrics_token`, `metrics_token_file` or `GARAGE_METRICS_TOKEN` (env) {#admin_metrics_token}
The token for accessing the Metrics endpoint. If this token is not set, the The token for accessing the Metrics endpoint. If this token is not set, the
Metrics endpoint can be accessed without access control. Metrics endpoint can be accessed without access control.
@ -534,7 +594,7 @@ You can use any random string for this value. We recommend generating a random t
`metrics_token_file` and the `GARAGE_METRICS_TOKEN` environment variable are supported since Garage `v0.8.2`. `metrics_token_file` and the `GARAGE_METRICS_TOKEN` environment variable are supported since Garage `v0.8.2`.
### `admin_token`, `admin_token_file` or `GARAGE_ADMIN_TOKEN` (env) #### `admin_token`, `admin_token_file` or `GARAGE_ADMIN_TOKEN` (env) {#admin_token}
The token for accessing all of the other administration endpoints. If this The token for accessing all of the other administration endpoints. If this
token is not set, access to these endpoints is disabled entirely. token is not set, access to these endpoints is disabled entirely.
@ -545,7 +605,7 @@ You can use any random string for this value. We recommend generating a random t
`admin_token_file` and the `GARAGE_ADMIN_TOKEN` environment variable are supported since Garage `v0.8.2`. `admin_token_file` and the `GARAGE_ADMIN_TOKEN` environment variable are supported since Garage `v0.8.2`.
### `trace_sink` #### `trace_sink` {#admin_trace_sink}
Optionally, the address of an OpenTelemetry collector. If specified, Optionally, the address of an OpenTelemetry collector. If specified,
Garage will send traces in the OpenTelemetry format to this endpoint. These Garage will send traces in the OpenTelemetry format to this endpoint. These

View File

@ -52,7 +52,7 @@ This is particularly usefull when nodes are far from one another and talk to one
Garage supports a variety of replication modes, with 1 copy, 2 copies or 3 copies of your data, Garage supports a variety of replication modes, with 1 copy, 2 copies or 3 copies of your data,
and with various levels of consistency, in order to adapt to a variety of usage scenarios. and with various levels of consistency, in order to adapt to a variety of usage scenarios.
Read our reference page on [supported replication modes](@/documentation/reference-manual/configuration.md#replication-mode) Read our reference page on [supported replication modes](@/documentation/reference-manual/configuration.md#replication_mode)
to select the replication mode best suited to your use case (hint: in most cases, `replication_mode = "3"` is what you want). to select the replication mode best suited to your use case (hint: in most cases, `replication_mode = "3"` is what you want).
### Web server for static websites ### Web server for static websites

View File

@ -1,31 +1,5 @@
{ {
"nodes": { "nodes": {
"cargo2nix": {
"inputs": {
"flake-compat": [
"flake-compat"
],
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
],
"rust-overlay": "rust-overlay"
},
"locked": {
"lastModified": 1666087781,
"narHash": "sha256-trKVdjMZ8mNkGfLcY5LsJJGtdV3xJDZnMVrkFjErlcs=",
"owner": "Alexis211",
"repo": "cargo2nix",
"rev": "a7a61179b66054904ef6a195d8da736eaaa06c36",
"type": "github"
},
"original": {
"owner": "Alexis211",
"repo": "cargo2nix",
"rev": "a7a61179b66054904ef6a195d8da736eaaa06c36",
"type": "github"
}
},
"flake-compat": { "flake-compat": {
"locked": { "locked": {
"lastModified": 1688025799, "lastModified": 1688025799,
@ -46,54 +20,19 @@
"systems": "systems" "systems": "systems"
}, },
"locked": { "locked": {
"lastModified": 1681202837, "lastModified": 1701680307,
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "cfacdce06f30d2b68473a46042957675eebb3401", "rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "numtide", "id": "flake-utils",
"repo": "flake-utils", "type": "indirect"
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1681202837,
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
} }
}, },
"nixpkgs": { "nixpkgs": {
"locked": {
"lastModified": 1682109806,
"narHash": "sha256-d9g7RKNShMLboTWwukM+RObDWWpHKaqTYXB48clBWXI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "2362848adf8def2866fabbffc50462e929d7fffb",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1682423271, "lastModified": 1682423271,
"narHash": "sha256-WHhl1GiOij1ob4cTLL+yhqr+vFOUH8E5wAX8Ir8fvjE=", "narHash": "sha256-WHhl1GiOij1ob4cTLL+yhqr+vFOUH8E5wAX8Ir8fvjE=",
@ -111,32 +50,9 @@
}, },
"root": { "root": {
"inputs": { "inputs": {
"cargo2nix": "cargo2nix",
"flake-compat": "flake-compat", "flake-compat": "flake-compat",
"flake-utils": [ "flake-utils": "flake-utils",
"cargo2nix",
"flake-utils"
],
"nixpkgs": "nixpkgs_2"
}
},
"rust-overlay": {
"inputs": {
"flake-utils": "flake-utils_2",
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1682389182,
"narHash": "sha256-8t2nmFnH+8V48+IJsf8AK51ebXNlVbOSVYOpiqJKvJE=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "74f1a64dd28faeeb85ef081f32cad2989850322c",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
} }
}, },
"systems": { "systems": {
@ -153,21 +69,6 @@
"repo": "default", "repo": "default",
"type": "github" "type": "github"
} }
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
} }
}, },
"root": "root", "root": "root",

View File

@ -8,50 +8,23 @@
inputs.flake-compat.url = "github:nix-community/flake-compat"; inputs.flake-compat.url = "github:nix-community/flake-compat";
inputs.cargo2nix = { outputs = { self, nixpkgs, flake-utils, ... }:
# As of 2022-10-18: two small patches over unstable branch, one for clippy and one to fix feature detection
url = "github:Alexis211/cargo2nix/a7a61179b66054904ef6a195d8da736eaaa06c36";
# As of 2023-04-25:
# - my two patches were merged into unstable (one for clippy and one to "fix" feature detection)
# - rustc v1.66
# url = "github:cargo2nix/cargo2nix/8fb57a670f7993bfc24099c33eb9c5abb51f29a2";
# Rust overlay as of 2023-04-25
inputs.rust-overlay.url =
"github:oxalica/rust-overlay/74f1a64dd28faeeb85ef081f32cad2989850322c";
inputs.nixpkgs.follows = "nixpkgs";
inputs.flake-compat.follows = "flake-compat";
};
inputs.flake-utils.follows = "cargo2nix/flake-utils";
outputs = { self, nixpkgs, cargo2nix, flake-utils, ... }:
let let
git_version = self.lastModifiedDate; gitVersion = self.lastModifiedDate;
compile = import ./nix/compile.nix; compile = import ./nix/compile.nix;
in in
flake-utils.lib.eachDefaultSystem (system: flake-utils.lib.eachDefaultSystem (system:
let pkgs = nixpkgs.legacyPackages.${system}; let pkgs = nixpkgs.legacyPackages.${system};
in { in {
packages = { packages = {
default = (compile { default = (import ./default.nix {
inherit system git_version; inherit gitVersion;
pkgsSrc = nixpkgs; buildSystem = system;
cargo2nixOverlay = cargo2nix.overlays.default;
release = true; release = true;
}).workspace.garage { compileMode = "build"; }; }).build;
}; };
devShell = (compile { devShell = (import ./shell.nix {
inherit system git_version; buildSystem = system;
pkgsSrc = nixpkgs; }).rust;
cargo2nixOverlay = cargo2nix.overlays.default;
release = false;
}).workspaceShell { packages = with pkgs; [
rustfmt
clang
mold
]; };
}); });
} }

View File

@ -1,6 +1,6 @@
{ path ? "/../aws-list.txt", }: { path ? "/../aws-list.txt", }:
with import ./common.nix; with import ./pkgs.nix;
let let
pkgs = import pkgsSrc { }; pkgs = import pkgsSrc { };
lib = pkgs.lib; lib = pkgs.lib;

View File

@ -1,17 +0,0 @@
let
lock = builtins.fromJSON (builtins.readFile ../flake.lock);
inherit (lock.nodes.flake-compat.locked) owner repo rev narHash;
flake-compat = fetchTarball {
url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz";
sha256 = narHash;
};
flake = (import flake-compat { system = builtins.currentSystem; src = ../.; });
in
rec {
pkgsSrc = flake.defaultNix.inputs.nixpkgs;
cargo2nix = flake.defaultNix.inputs.cargo2nix;
cargo2nixOverlay = cargo2nix.overlays.default;
}

8
nix/pkgs.nix Normal file
View File

@ -0,0 +1,8 @@
let
lock = builtins.fromJSON (builtins.readFile ../flake.lock);
inherit (lock.nodes.nixpkgs.locked) owner repo rev narHash;
in
fetchTarball {
url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz";
sha256 = narHash;
}

View File

@ -1,12 +1,12 @@
{ system ? builtins.currentSystem, }: {
buildSystem ? builtins.currentSystem,
targetSystem ? buildSystem,
}:
with import ./nix/common.nix; with import ./nix/pkgs.nix;
let let
pkgs = import pkgsSrc { inherit (import ./default.nix { inherit buildSystem targetSystem; }) pkgs pkgsCross buildEnv;
inherit system;
overlays = [ cargo2nixOverlay ];
};
kaniko = (import ./nix/kaniko.nix) pkgs; kaniko = (import ./nix/kaniko.nix) pkgs;
manifest-tool = (import ./nix/manifest-tool.nix) pkgs; manifest-tool = (import ./nix/manifest-tool.nix) pkgs;
winscp = (import ./nix/winscp.nix) pkgs; winscp = (import ./nix/winscp.nix) pkgs;
@ -14,22 +14,13 @@ let
in { in {
# --- Rust Shell --- # --- Rust Shell ---
# Use it to compile Garage # Use it to compile Garage
rust = pkgs.mkShell { rust = pkgsCross.mkShell (buildEnv // {
nativeBuildInputs = with pkgs; [ inputsFrom = [
#rustPlatform.rust.rustc kaniko
rustPlatform.rust.cargo manifest-tool
clang winscp
mold
#clippy
rustfmt
#perl
#protobuf
#pkg-config
#openssl
file
#cargo2nix.packages.x86_64-linux.cargo2nix
]; ];
}; });
# --- Integration shell --- # --- Integration shell ---
# Use it to test Garage with common S3 clients # Use it to test Garage with common S3 clients

View File

@ -45,7 +45,7 @@ http = "0.2"
httpdate = "1.0" httpdate = "1.0"
http-range = "0.1" http-range = "0.1"
hyper = { version = "0.14", features = ["server", "http1", "runtime", "tcp", "stream"] } hyper = { version = "0.14", features = ["server", "http1", "runtime", "tcp", "stream"] }
hyperlocal = { version = "0.8.0", default-features = false, features = ["server"] } #hyperlocal = { version = "0.8.0", default-features = false, features = ["server"] }
multer = "2.0" multer = "2.0"
percent-encoding = "2.1.0" percent-encoding = "2.1.0"
roxmltree = "0.18" roxmltree = "0.18"

View File

@ -1,5 +1,4 @@
use std::fs::{self, Permissions}; //use std::fs::{self, Permissions};
use std::os::unix::fs::PermissionsExt;
use std::sync::Arc; use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
@ -12,9 +11,9 @@ use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server}; use hyper::{Body, Request, Response, Server};
use hyper::{HeaderMap, StatusCode}; use hyper::{HeaderMap, StatusCode};
use hyperlocal::UnixServerExt; //use hyperlocal::UnixServerExt;
use tokio::net::UnixStream; //use tokio::net::UnixStream;
use opentelemetry::{ use opentelemetry::{
global, global,
@ -114,18 +113,18 @@ impl<A: ApiHandler> ApiServer<A> {
} }
}); });
let unix_service = make_service_fn(|_: &UnixStream| { //let unix_service = make_service_fn(|_: &UnixStream| {
let this = self.clone(); // let this = self.clone();
let path = bind_addr.to_string(); // let path = bind_addr.to_string();
async move { // async move {
Ok::<_, GarageError>(service_fn(move |req: Request<Body>| { // Ok::<_, GarageError>(service_fn(move |req: Request<Body>| {
let this = this.clone(); // let this = this.clone();
this.handler(req, path.clone()) // this.handler(req, path.clone())
})) // }))
} // }
}); //});
info!( info!(
"{} API server listening on {}", "{} API server listening on {}",
@ -140,23 +139,24 @@ impl<A: ApiHandler> ApiServer<A> {
.with_graceful_shutdown(shutdown_signal) .with_graceful_shutdown(shutdown_signal)
.await? .await?
} }
UnixOrTCPSocketAddress::UnixSocket(ref path) => { UnixOrTCPSocketAddress::UnixSocket(_path) => {
if path.exists() { panic!("Unix sockets are not supported in this fork") // TODO(mediocregopher)
fs::remove_file(path)? } //UnixOrTCPSocketAddress::UnixSocket(ref path) => {
} // use std::os::unix::fs::PermissionsExt;
// remove_unix_socket_if_present(path).await?;
let bound = Server::bind_unix(path)?; // let bound = Server::bind_unix(path)?;
fs::set_permissions( // fs::set_permissions(
path, // path,
Permissions::from_mode(unix_bind_addr_mode.unwrap_or(0o222)), // Permissions::from_mode(unix_bind_addr_mode.unwrap_or(0o222)),
)?; // )?;
bound // bound
.serve(unix_service) // .serve(unix_service)
.with_graceful_shutdown(shutdown_signal) // .with_graceful_shutdown(shutdown_signal)
.await?; // .await?;
} //}
}; };
Ok(()) Ok(())

View File

@ -344,7 +344,7 @@ impl ApiHandler for S3ApiServer {
bucket_id, bucket_id,
key, key,
upload_id, upload_id,
part_number_marker: part_number_marker.map(|p| p.clamp(1, 10000)), part_number_marker: part_number_marker.map(|p| p.min(10000)),
max_parts: max_parts.unwrap_or(1000).clamp(1, 1000), max_parts: max_parts.unwrap_or(1000).clamp(1, 1000),
}, },
) )

View File

@ -771,11 +771,7 @@ impl BlockManagerLocked {
// Now, we do an fsync on the containing directory, to ensure that the rename // Now, we do an fsync on the containing directory, to ensure that the rename
// is persisted properly. See: // is persisted properly. See:
// http://thedjbway.b0llix.net/qmail/syncdir.html // http://thedjbway.b0llix.net/qmail/syncdir.html
let dir = fs::OpenOptions::new() let dir = fs::OpenOptions::new().read(true).open(directory).await?;
.read(true)
.mode(0)
.open(directory)
.await?;
dir.sync_all().await?; dir.sync_all().await?;
drop(dir); drop(dir);
} }

View File

@ -3,7 +3,6 @@ use std::collections::HashMap;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::net::{IpAddr, SocketAddr}; use std::net::{IpAddr, SocketAddr};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::atomic::Ordering;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@ -198,6 +197,20 @@ pub fn read_node_id(metadata_dir: &Path) -> Result<NodeID, Error> {
Ok(NodeID::from_slice(&key[..]).unwrap()) Ok(NodeID::from_slice(&key[..]).unwrap())
} }
#[cfg(not(windows))]
fn set_private_key_perms(path: &Path) -> Result<(), Error> {
use std::os::unix::fs::PermissionsExt;
let perm = std::fs::Permissions::from_mode(0o600);
std::fs::set_permissions(path, perm)?;
Ok(())
}
#[cfg(windows)]
fn set_private_key_perms(_path: &Path) -> Result<(), Error> {
// TODO(mediocregopher) figure out how to do this, but it's not strictly necessary
Ok(())
}
pub fn gen_node_key(metadata_dir: &Path) -> Result<NodeKey, Error> { pub fn gen_node_key(metadata_dir: &Path) -> Result<NodeKey, Error> {
let mut key_file = metadata_dir.to_path_buf(); let mut key_file = metadata_dir.to_path_buf();
key_file.push("node_key"); key_file.push("node_key");
@ -222,11 +235,8 @@ pub fn gen_node_key(metadata_dir: &Path) -> Result<NodeKey, Error> {
let (pubkey, key) = ed25519::gen_keypair(); let (pubkey, key) = ed25519::gen_keypair();
{ {
use std::os::unix::fs::PermissionsExt;
let mut f = std::fs::File::create(key_file.as_path())?; let mut f = std::fs::File::create(key_file.as_path())?;
let mut perm = f.metadata()?.permissions(); set_private_key_perms(key_file.as_path())?;
perm.set_mode(0o600);
std::fs::set_permissions(key_file.as_path(), perm)?;
f.write_all(&key[..])?; f.write_all(&key[..])?;
} }
@ -890,6 +900,18 @@ impl NodeStatus {
} }
} }
#[cfg(windows)]
fn update_disk_usage(
&mut self,
_meta_dir: &Path,
_data_dir: &DataDirEnum,
_metrics: &SystemMetrics,
) {
// TODO(mediocregopher) it'd be nice to have this for windows too, but it seems to only be
// used for OpenTelemetry so it's not a real requirement.
}
#[cfg(not(windows))]
fn update_disk_usage( fn update_disk_usage(
&mut self, &mut self,
meta_dir: &Path, meta_dir: &Path,
@ -897,6 +919,7 @@ impl NodeStatus {
metrics: &SystemMetrics, metrics: &SystemMetrics,
) { ) {
use nix::sys::statvfs::statvfs; use nix::sys::statvfs::statvfs;
use std::sync::atomic::Ordering;
let mount_avail = |path: &Path| match statvfs(path) { let mount_avail = |path: &Path| match statvfs(path) {
Ok(x) => { Ok(x) => {
let avail = x.blocks_available() as u64 * x.fragment_size() as u64; let avail = x.blocks_available() as u64 * x.fragment_size() as u64;
@ -955,6 +978,7 @@ impl NodeStatus {
} }
} }
#[cfg(not(windows))]
fn get_default_ip() -> Option<IpAddr> { fn get_default_ip() -> Option<IpAddr> {
pnet_datalink::interfaces() pnet_datalink::interfaces()
.iter() .iter()
@ -963,6 +987,11 @@ fn get_default_ip() -> Option<IpAddr> {
.map(|a| a.ip()) .map(|a| a.ip())
} }
#[cfg(windows)]
fn get_default_ip() -> Option<IpAddr> {
None
}
async fn resolve_peers(peers: &[String]) -> Vec<(NodeID, SocketAddr)> { async fn resolve_peers(peers: &[String]) -> Vec<(NodeID, SocketAddr)> {
let mut ret = vec![]; let mut ret = vec![];

View File

@ -27,7 +27,7 @@ futures = "0.3"
http = "0.2" http = "0.2"
hyper = { version = "0.14", features = ["server", "http1", "runtime", "tcp", "stream"] } hyper = { version = "0.14", features = ["server", "http1", "runtime", "tcp", "stream"] }
hyperlocal = { version = "0.8.0", default-features = false, features = ["server"] } #hyperlocal = { version = "0.8.0", default-features = false, features = ["server"] }
tokio = { version = "1.0", default-features = false, features = ["net"] } tokio = { version = "1.0", default-features = false, features = ["net"] }

View File

@ -1,5 +1,5 @@
use std::fs::{self, Permissions}; //use std::fs::{self, Permissions};
use std::os::unix::prelude::PermissionsExt; //use std::os::unix::prelude::PermissionsExt;
use std::{convert::Infallible, sync::Arc}; use std::{convert::Infallible, sync::Arc};
use futures::future::Future; use futures::future::Future;
@ -11,9 +11,9 @@ use hyper::{
Body, Method, Request, Response, Server, StatusCode, Body, Method, Request, Response, Server, StatusCode,
}; };
use hyperlocal::UnixServerExt; //use hyperlocal::UnixServerExt;
use tokio::net::UnixStream; //use tokio::net::UnixStream;
use opentelemetry::{ use opentelemetry::{
global, global,
@ -100,18 +100,18 @@ impl WebServer {
} }
}); });
let unix_service = make_service_fn(|_: &UnixStream| { //let unix_service = make_service_fn(|_: &UnixStream| {
let web_server = web_server.clone(); // let web_server = web_server.clone();
let path = addr.to_string(); // let path = addr.to_string();
async move { // async move {
Ok::<_, Error>(service_fn(move |req: Request<Body>| { // remove_unix_socket_if_present(&path).await.expect("could not remove existing unix socket");
let web_server = web_server.clone(); // Ok::<_, Error>(service_fn(move |req: Request<Body>| {
// let web_server = web_server.clone();
web_server.handle_request(req, path.clone()) // web_server.handle_request(req, path.clone())
})) // }))
} // }
}); //});
info!("Web server listening on {}", addr); info!("Web server listening on {}", addr);
@ -122,20 +122,22 @@ impl WebServer {
.with_graceful_shutdown(shutdown_signal) .with_graceful_shutdown(shutdown_signal)
.await? .await?
} }
UnixOrTCPSocketAddress::UnixSocket(ref path) => { UnixOrTCPSocketAddress::UnixSocket(_path) => {
if path.exists() { panic!("Unix sockets are not supported in this fork") // TODO(mediocregopher)
fs::remove_file(path)? } //UnixOrTCPSocketAddress::UnixSocket(ref path) => {
} // if path.exists() {
// fs::remove_file(path)?
// }
let bound = Server::bind_unix(path)?; // let bound = Server::bind_unix(path)?;
fs::set_permissions(path, Permissions::from_mode(0o222))?; // fs::set_permissions(path, Permissions::from_mode(0o222))?;
bound // bound
.serve(unix_service) // .serve(unix_service)
.with_graceful_shutdown(shutdown_signal) // .with_graceful_shutdown(shutdown_signal)
.await?; // .await?;
} //}
}; };
Ok(()) Ok(())