Compare commits

..

189 Commits

Author SHA1 Message Date
Alex 5254750658 Merge pull request 'Add TLS support for Consul discovery + refactoring' (#405) from consul-tls into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/405
2022-10-18 20:20:55 +00:00
Alex Auvolat 57b5c2c754 Change reqwest rustls features 2022-10-18 22:11:27 +02:00
Alex Auvolat 8bc5caf7aa Fix issue with 'http(s)://' prefix 2022-10-18 21:17:11 +02:00
Alex Auvolat 2da8786f54 move things around 2022-10-18 19:13:52 +02:00
Alex Auvolat 5d8d393054 Load TLS certificates only once 2022-10-18 19:11:16 +02:00
Alex Auvolat 002b9fc50c Add TLS support for Consul discovery + refactoring 2022-10-18 18:38:20 +02:00
Alex 5670599372 Merge pull request 'Use status code 204 No Content for empty responses' (#403) from tobikris/garage:http-no-content into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/403
2022-10-18 14:20:44 +00:00
Alex 7bc9fd34b2 Merge pull request 'upgrade Nix toolchain' (#400) from upgrade-toolchain into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/400
2022-10-18 14:16:52 +00:00
Alex Auvolat a54a63c491 Add function to upload a build and its dependencies to the cache
to faster bootstrap new runner nodes
2022-10-18 14:19:19 +02:00
Tobias Krischer f1c96d108c update k2v docs for status 204 changes 2022-10-18 13:50:56 +02:00
Alex Auvolat 8fc93abc79 Some things are now in result-bin 2022-10-18 13:39:21 +02:00
Alex Auvolat 667ca9d3e3 Cleanup nix scripts 2022-10-18 12:48:31 +02:00
Alex Auvolat 6a5eba0b72 Add garage_db test to CI 2022-10-18 12:33:35 +02:00
Alex Auvolat 00cf076412 Fix cargo2nix feature discovery 2022-10-18 12:15:45 +02:00
Alex Auvolat 7c0c229934 move refresh_toolchain 2022-10-18 12:15:31 +02:00
Tobias Krischer 7865003323 Use status code 204 No Content for empty responses 2022-10-17 10:55:26 +02:00
Quentin 4582a8f34a Merge pull request 'Update 'doc/book/reference-manual/features.md'' (#402) from borgified/garage:borgified-patch-1 into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/402
2022-10-16 07:41:32 +00:00
borgified 8e442001b9 Update 'doc/book/reference-manual/features.md'
typo
2022-10-16 07:13:21 +00:00
Alex Auvolat c050a59fd0 Fix conditional testing in garage_db 2022-10-14 18:27:18 +02:00
Alex Auvolat fcaee3bea0 definitively expunge openssl from dependencies everywhere 2022-10-14 18:10:36 +02:00
Alex Auvolat e89e047c5a Fix i386 build with custom toolchain (armv6 unknown state) 2022-10-14 18:10:24 +02:00
Alex Auvolat 8d04ae7014 cargo2nix unstable (patched), rust 1.63.0, nixpkgs 22.05 (32-bit builds are broken) 2022-10-14 14:30:48 +02:00
Alex a096ced355 Merge pull request 'Fix instant substractions that might have panicked' (#398) from fix-time into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/398
2022-10-02 16:41:06 +02:00
Alex e21b672c96 Merge pull request 'Add helm chart' (#331) from chemicstry/garage:helm_chart into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/331
Reviewed-by: maximilien <me@mricher.fr>
2022-10-02 16:40:54 +02:00
Maximilien R db0c8b3980 Updates values.yml with some opinionated and untested defaults 2022-09-30 18:46:57 +02:00
Maximilien R 6dba7dadf4 Add missing ClusterRole and bindings for CRDs 2022-09-30 18:46:57 +02:00
chemicstry d2c937a931 Fix typo 2022-09-30 18:46:57 +02:00
chemicstry 744c3b4d94 Update docs 2022-09-30 18:46:57 +02:00
chemicstry b71fa2ddf4 Generate random RPC secret if not provided 2022-09-30 18:46:57 +02:00
chemicstry 37a73d7d37 Move documentation to book 2022-09-30 18:46:57 +02:00
chemicstry d0f08c254e Add secret to overrides 2022-09-30 18:46:57 +02:00
chemicstry fa52558ca1 Add configuration instructions to README 2022-09-30 18:46:57 +02:00
chemicstry 131cc2532b Cleanup values.yaml 2022-09-30 18:46:57 +02:00
chemicstry a93dcce841 Add helm chart 2022-09-30 18:46:57 +02:00
Alex b17d59cfab Merge pull request 'Document db_engine' (#399) from doc-0.8 into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/399
2022-09-29 17:29:44 +02:00
Alex Auvolat ad917ffd3f Fix instant substractions that might have panicked 2022-09-29 15:53:54 +02:00
Alex 497164d782 Merge pull request 'Shutdown properly on SIGTERM/SIGHUP and on Windows signals' (#397) from handle-sigterm into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/397
2022-09-28 12:16:55 +02:00
Alex Auvolat 1f97ce37e6 Shutdown properly on SIGTERM/SIGHUP and on Windows signals 2022-09-28 10:41:59 +02:00
Alex Auvolat 0ab0d3cc29 Document db_engine 2022-09-27 16:52:36 +02:00
Alex 2197753dfd Merge pull request 'Add step to generate multi-arch Docker container in CI' (#393) from multi-arch-container into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/393
2022-09-27 11:55:49 +02:00
Alex 3f95a0f717 Merge pull request 'Enable k2v feature flag by default in CI' (#302) from k2v into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/302
2022-09-27 11:38:23 +02:00
Alex 7291747a28 Merge pull request 'Documentation changes for v0.8' (#394) from doc-0.8 into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/394
2022-09-27 11:37:12 +02:00
Alex Auvolat d104ae8711 Add step to generate multi-arch Docker container in CI 2022-09-26 19:09:55 +02:00
Alex Auvolat 194e8be1bb Update docker image links 2022-09-26 18:01:17 +02:00
Alex Auvolat 69bcc813de Add garage v0.8 migration guide 2022-09-26 17:46:38 +02:00
Alex ea7a571d88 Merge pull request 'Fix span name for api server requests' (#392) from fix-span-name into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/392
2022-09-26 16:57:37 +02:00
Alex Auvolat 1778e4b318 Fix span name for api server requests 2022-09-26 16:21:30 +02:00
Alex af2b2f26b4 Merge pull request 'Update README (fix #230)' (#391) from new-readme into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/391
2022-09-21 13:28:52 +02:00
Alex Auvolat a3758dc4c4 Update README 2022-09-21 12:53:02 +02:00
Alex Auvolat e89f880694 Enable k2v feature flag in CI 2022-09-20 17:54:41 +02:00
Alex fc85508648 Merge pull request 'Initialize metrics exporter earlier (fix #389)' (#390) from fix-metrics into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/390
2022-09-20 17:53:46 +02:00
Alex Auvolat 782630fc27 Initialize metrics exporter earlier (fix #389) 2022-09-20 17:50:22 +02:00
Alex 7a901f7aab Merge pull request 'RPC performance changes' (#387) from configurable-timeouts into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/387
2022-09-20 16:17:23 +02:00
Alex Auvolat ded444f6c9 Ability to have custom timeouts in request strategy (not used) 2022-09-20 16:01:41 +02:00
Alex Auvolat 357b72f4ff Merge branch 'main' into configurable-timeouts 2022-09-20 15:19:58 +02:00
Alex 2c312e9cbd Merge pull request 'Change a warn! into a debug!' (#388) from less-noise into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/388
2022-09-20 11:57:52 +02:00
Alex Auvolat 1f7b050b7d Change a warn! into a debug! 2022-09-20 11:49:48 +02:00
Alex Auvolat 56592e1853 RPC performance changes
- configurable ping timeout
- single, much higher, configurable RPC timeout
- no more concurrency semaphore
2022-09-19 20:31:00 +02:00
Alex fbd32933ea Merge pull request 'Faster GetObject workflow for getting entire objects' (#386) from faster-get into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/386
2022-09-19 15:24:06 +02:00
Alex Auvolat 5d4b6f2173 Faster GetObject workflow for getting entire objects 2022-09-19 12:19:59 +02:00
Alex 4fba06d62e Merge pull request 'updates to documentation for v0.8' (#385) from doc-0.8 into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/385
2022-09-19 10:45:10 +02:00
Alex Auvolat 1d0a610690 Finish writing about Garage features, and fix from-source instructions 2022-09-15 13:23:57 +02:00
Alex Auvolat f6aebefcc9 Some work on documentation towards v0.8 2022-09-14 19:31:13 +02:00
Alex 89b8087ba8 Merge pull request 'Properly return HTTP 204 when deleting non-existent object (fix #227)' (#384) from deleteobject-204 into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/384
2022-09-14 17:16:39 +02:00
Alex Auvolat 76f42a1a2b Properly return HTTP 204 when deleting non-existent object (fix #227) 2022-09-14 17:07:55 +02:00
Alex 82600acf77 Merge pull request 'Allow for hostnames in bootstrap_peers and rpc_public_addr (fix #353)' (#383) from resolve-peer-names into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/383
2022-09-14 16:37:18 +02:00
Alex Auvolat e46dc2a8ef Allow for hostnames in bootstrap_peers and rpc_public_addr (fix #353) 2022-09-14 16:09:38 +02:00
Alex 80fdbfb0aa Merge pull request 'various fixes for v0.8.0' (#380) from various-fixes-for-0.8 into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/380
2022-09-13 16:49:05 +02:00
Alex Auvolat ab722cb40f Add checks on replication_factor of layouts we use (fix #363, fix #364) 2022-09-13 16:22:23 +02:00
Alex Auvolat 38be811b1c Fix clippy lint that says we should implement Eq 2022-09-13 16:08:00 +02:00
Alex Auvolat 44733474bb Remove/change println! in server code (fix #358) 2022-09-13 16:01:55 +02:00
Alex Auvolat 07febd3ecd Ensure data dir is created immediately when Garage starts (fix #349) 2022-09-13 15:57:27 +02:00
Alex 11bdc971e2 Merge pull request 'use netapp streaming body' (#343) from netapp-stream-body into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/343
2022-09-13 15:26:08 +02:00
Alex Auvolat ff30891999 Use streaming block API for get with Range requests 2022-09-13 15:13:07 +02:00
Alex Auvolat 28a4af73ca Use netapp 0.5 published from crates.io 2022-09-13 13:11:44 +02:00
Alex Auvolat b823151a0b improvements in block manager 2022-09-12 16:57:38 +02:00
Alex 309d7aef3f Merge pull request 'performance improvements' (#342) from lx-perf-improvements into main
Performance improvements included in this PR:

- [x] Use `Bytes` at a few places where appropriate, instead of `Vec<u8>`, to reduce the number of copies
  - [x] StreamChunker now accumulates incoming slices in a `Vec<Bytes>` instead of a `VecDeque<u8>`. Replaces calls to `.extend()` and `.drain()` that were quite costly by a simple `concat()` on a vec of slices which is much more optimized
- [x] Hashing (b2, sha256, md5) is now done on a Tokio thread dedicated to cpu-intensive tasks, using `spawn_blocking`
- [x] Block manager now uses 256 independant locks instead of one big lock for writing, reduces contention when writing several/many objects in parallel
- [x] Better LMDB defaults: we now put flags `NoSync` and `NoMetaSync` to avoid `fsync` at each transaction (extremely slow). Also increased number of LMDB readers to accomodate more intensive workloads

Other changes included in this PR:

- [x] Update to hashing and MAC crates: md5 and sha2 from 0.9 to 0.10, hmac from 0.10 to 0.12
- [x] switch to `tracing_subscriber` for logs, which allows to have timing of each event

Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/342
2022-09-12 16:38:43 +02:00
Alex Auvolat f91fab8582 Simplify+improve async hasher by using bounded channel 2022-09-12 16:23:43 +02:00
Alex Auvolat 7f54706b95 Merge branch 'lx-perf-improvements' into netapp-stream-body 2022-09-08 15:50:56 +02:00
Alex Auvolat d9d199a6c9 Merge branch 'main' into lx-perf-improvements 2022-09-08 15:49:17 +02:00
Alex 03c40a0b24 Merge pull request 'Reorganize dependencies' (#373) from improve-deps into main
This PR includes work from @jirutka :

- [x] Allow linking against system-provided libraries (libsodium, libsqlite, libzstd) #370
- [x] Make OTLP exporter optional and allow building without Prometheus exporter (/metrics) #372

And also:

- [x] Update `.nix` files
- [x] Remove heed default-features
- [x] Bump versions of all Garage crates to 0.8.0
- [x] Make db engines (lmdb, sled, sqlite) optionnal
- [x] Add documentation for available features
- [x] Directly include code of previous versions used for migration in order to reduce dependencies
- [x] Read variable `GIT_VERSION` from garage main instead of in crate garage_util to make builds faster
- [x] Report features used in the build somewhere? (in `garage --version` or something)
- [x] Check we `warn!` correctly if we try to use deactivated feature
- [x] Allow not to launch S3 endpoint if not in config

Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/373
2022-09-08 15:45:09 +02:00
Alex Auvolat ceb1f0229a Move version back into util 2022-09-07 18:36:46 +02:00
Alex Auvolat f310fce34b Inject GIT_VERSION even later 2022-09-07 18:30:15 +02:00
Alex Auvolat 06df301de5 Fix merge 2022-09-07 18:16:01 +02:00
Alex Auvolat 8adc654713 Merge branch 'main' into improve-deps 2022-09-07 18:13:27 +02:00
Alex Auvolat 107853334b Fix build error 2022-09-07 18:10:19 +02:00
Alex Auvolat 1449204439 Add warnings when features are not included in build 2022-09-07 18:02:13 +02:00
Alex Auvolat 2e00809af5 Error messages when system-libs XOR bundled-libs != 1 2022-09-07 17:57:12 +02:00
Alex Auvolat 2559f63e9b Make all HTTP services optionnal 2022-09-07 17:54:16 +02:00
Alex Auvolat 28d86e7602 Report build features in garage --help 2022-09-07 17:05:21 +02:00
Alex Auvolat db61f41030 Move GIT_VERSION injection later in build chain to reduce build times 2022-09-07 11:59:56 +02:00
Alex Auvolat 907054775d Faster copy, better get error message 2022-09-06 22:25:23 +02:00
Alex Auvolat 6b958979bd Merge branch 'lx-perf-improvements' into netapp-stream-body 2022-09-06 22:13:01 +02:00
Alex Auvolat d23b3a14fc Merge branch 'main' into lx-perf-improvements 2022-09-06 21:53:37 +02:00
Alex Auvolat 4024822585 Update netapp to lastest git version with LAS scheduling 2022-09-06 19:45:00 +02:00
Alex Auvolat c2cc08852b Reenable node ordering 2022-09-06 19:31:42 +02:00
Alex Auvolat 6f02c36a89 cargo fmt 2022-09-06 17:59:41 +02:00
Alex Auvolat 0f5689c169 Include code from v0.5.1 directly to remove dependencies 2022-09-06 17:52:50 +02:00
Alex Auvolat 1e92e9f782 Disable k2v tests when feature is disabled 2022-09-06 17:29:46 +02:00
Alex Auvolat 431dee050f Remove opentelemetry-otlp dep in api/ 2022-09-06 17:25:44 +02:00
Alex Auvolat 2c2b93acdf Update Nix files with optional db engines 2022-09-06 17:20:10 +02:00
Alex Auvolat bbb970965c Document available build features 2022-09-06 17:16:45 +02:00
Alex Auvolat b886c75450 Make all DB engines optional build features 2022-09-06 17:09:43 +02:00
Alex Auvolat 48ffaaadfc Bump versions to 0.8.0 (compatibility is broken already) 2022-09-06 16:47:56 +02:00
Alex Auvolat 7de53a4d66 Force disable pkg-config for libsodum-sys and libzstd-sys 2022-09-06 16:41:58 +02:00
Alex Auvolat 8d77a76df1 Update .nix files 2022-09-06 15:49:41 +02:00
Alex Auvolat 454d8474ef Fix clippy 2022-09-06 15:43:50 +02:00
Alex ed7796924b Merge pull request 'Make OTLP exporter optional and allow building without Prometheus exporter (/metrics)' (#372) from jirutka/garage:telemetry-and-metrics into improve-deps
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/372
Reviewed-by: Alex <alex@adnab.me>
2022-09-06 15:11:30 +02:00
Jakub Jirutka ea36b9ff90 Allow building without Prometheus exporter (/metrics endpoint)
prometheus and opentelemetry-prometheus add 7 extra dependencies in
total and increases the size of the garage binary by ~7 % (with
fat LTO).
2022-09-06 01:15:09 +02:00
Jakub Jirutka e7af006c1c Make OTLP exporter optional via feature "telemetry-otlp"
opentelemetry-otlp add 48 (!) extra dependencies and increases the
size of the garage binary by ~11 % (with fat LTO).
2022-09-06 01:14:47 +02:00
Jakub Jirutka db72812f01 Use the new cargo feature resolver "2"
Garage currently uses the legacy resolver "1". The new one is used
by default if the root package specifies 'edition = 2021', which
Garage does not (yet).

The problem with the legacy resolver is, among others, that features
enabled by dev-dependencies are propagated to normal dependencies.
This affects e.g. hyper - one of the dev-dependencies enables "http2"
feature that adds many extra dependencies. If we build garage without
opentelemetry-otlp (this is enabled in the following commit), there's
no normal dependency enabling "http2" feature.

See https://doc.rust-lang.org/cargo/reference/resolver.html#feature-resolver-version-2
2022-09-06 01:14:19 +02:00
Alex Auvolat 729a910e14 Remove Heed default features 2022-09-05 16:40:13 +02:00
Alex 9f5433db82 Merge pull request 'Update .drone.yml signature' (#374) from fix-drone-signature into main
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/374
2022-09-05 16:18:15 +02:00
Alex Auvolat fd8074ad9b Update .drone.yml signature 2022-09-05 16:09:01 +02:00
Alex Auvolat 07e6bcde85 Merge branch 'main' into lx-perf-improvements 2022-09-05 12:40:17 +02:00
Alex 0009fd136c Merge pull request 'Make block resync speed dynamically configurable' (#369) from resync-ajustable-speed into main
Included in this PR:

- [x] Small refactor, resync code is moved to a separate `block/resync.rs` file
- [x] Block resync tranquility is no longer in config file, it is set dynamically using `garage worker set resync-tranquility` (this parameter is persisted over Garage restarts)
- [x] Up to 4 block resync workers can be activated to run simultaneously to speed up big resyncs, this parameter is set dynamically using `garage worker set resync-n-workers`

Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/369
2022-09-05 12:35:08 +02:00
Jakub Jirutka 7511ba5530 Allow linking against system-provided libsqlite
Unfortunately, rusqlite uses the opposite logic for enabling/disabling
bundled libraries to others (libsodium-sys, zstd-sys). Cargo features
are very limited and doesn't allow to enable feature A in a dependency
iff feature B is disabled.

Note, lmdb-rkv-sys doesn't need any special treatment because it
automatically links against system liblmdb if found via pkgconf.

Linux distros should build garage with
`--no-default-features --features system-libs` to disable bundled-libs
and enable system-libs.
2022-09-03 19:15:57 +02:00
Jakub Jirutka a6e40b75ea Add feature "system-libs" to enable linking against system libraries
If this feature is enabled, libsodium-sys and zstd-sys will link
dynamically against system-provided libraries instead of building
and linking statically the bundled (possibly outdated and vulnerable)
copies of them. This feature is intended mainly for linux package
maintainers.
2022-09-03 18:44:34 +02:00
Alex Auvolat e1751c8a9c fix clippy 2022-09-02 17:24:26 +02:00
Alex Auvolat 5d4b937a00 Ability to have up to 4 concurrently working resync workers 2022-09-02 17:18:13 +02:00
Alex Auvolat 5e8baa433d Make BlockManagerLocked fully private again 2022-09-02 16:52:22 +02:00
Alex Auvolat 47be652a1f block manager: refactor: split resync into separate file 2022-09-02 16:47:15 +02:00
Alex Auvolat 943d76c583 Ability to dynamically set resync tranquility 2022-09-02 15:34:21 +02:00
Alex Auvolat 6226f5ceca Update to netapp 0.4.5 - fixed ping 2022-09-02 14:33:12 +02:00
Alex Auvolat 13b5f28c7e Make use of BytesBuf from new Netapp 2022-09-02 13:46:42 +02:00
Alex Auvolat 1ef87ac4cb cargo fmt 2022-09-02 13:38:29 +02:00
Alex Auvolat 99b532b85b Apply PRIO_SECONDARY to block data transfers 2022-09-01 16:35:43 +02:00
Alex Auvolat e648bf7b69 update cargo.nix 2022-09-01 16:31:04 +02:00
Alex Auvolat df094bd807 Less strict timeouts 2022-09-01 16:30:44 +02:00
Alex Auvolat f3bf34b6a1 update netapp: straming + fix-ping 2022-09-01 14:23:54 +02:00
Alex Auvolat bc977f9a7a Update to Netapp with OrderTag support and exploit OrderTags 2022-09-01 12:58:20 +02:00
Alex Auvolat 4b726b0941 netapp recv with unbounded channel removes deadlock 2022-09-01 09:47:28 +02:00
Alex Auvolat 70231d68b2 Fix bytes_read counter 2022-08-31 19:44:27 +02:00
Alex Auvolat e598231ca4 update netapp git commit 2022-08-31 19:27:25 +02:00
Alex Auvolat c9bc9d89de Merge branch 'lx-perf-improvements' into netapp-stream-body 2022-08-31 17:42:31 +02:00
Alex Auvolat eb97e13a6a update cargo.nix 2022-08-31 17:42:00 +02:00
Alex Auvolat efbca67ce4 Add env filter to tracing subscriber 2022-08-31 14:39:12 +02:00
Alex Auvolat 44cd98d2e4 Tracing-subscriber: write to stderr 2022-08-31 14:28:17 +02:00
Alex Auvolat dd5304f6fc Replace logging crate pretty_env_logger by tracing_subscriber::fmt 2022-08-31 14:24:41 +02:00
Alex Auvolat 322dafc761 Try to fix clippy 2022-08-29 17:32:45 +02:00
Alex Auvolat 5d065b8a0f cargo2nix fix to fetchCrateGit 2022-08-29 17:24:53 +02:00
Alex Auvolat 52749e28f7 Merge branch 'lx-perf-improvements' into netapp-stream-body 2022-08-29 16:48:43 +02:00
Alex Auvolat 4da67b0035 Update drone signature 2022-08-29 16:48:31 +02:00
Alex Auvolat 1921f4f7e6 Merge branch 'lx-perf-improvements' into netapp-stream-body 2022-08-29 16:45:05 +02:00
Alex Auvolat ebc20a8798 Merge branch 'main' into lx-perf-improvements 2022-08-29 16:44:13 +02:00
Quentin Dufour 532eca7ff9 Add some documentation for Caddy 2022-08-12 10:33:41 +02:00
Quentin Dufour 2c7bae935a Configure structopt to report the right version
By default, structopt reports the value provided by
the env var CARGO_PKG_VERSION, feeded by Cargo when reading
Cargo.toml. However for Garage we use a versioning based on git,
so we often report a version that is behind the real version.
In this commit, we create garage_util::version::garage() that
reports the right version and configure all structopt subcommands
to call this function instead of using the env var.
2022-08-11 10:21:45 +02:00
Quentin Dufour 8cd02639dc drone: set TARGET env as needed by "to_s3" func 2022-08-03 11:19:26 +02:00
Alex Auvolat e935861854 Factor out node request order selection logic & use in manager 2022-07-29 12:25:03 +02:00
Alex Auvolat f0ee3056d3 Update cargo.nix 2022-07-29 12:25:03 +02:00
Alex Auvolat 126b037307 update netapp 2022-07-29 12:25:03 +02:00
Alex Auvolat 33750c04ed Update cargo.nix 2022-07-29 12:25:03 +02:00
Alex Auvolat 68087ee13d Fix clippy 2022-07-29 12:25:03 +02:00
Alex Auvolat 605a630333 Use streaming in block manager 2022-07-29 12:25:02 +02:00
Alex Auvolat a35d4da721 update netapp to 0.5 2022-07-29 12:25:02 +02:00
Alex Auvolat 8e7e680afe First adaptation to WIP netapp with streaming body 2022-07-29 12:25:02 +02:00
Alex Auvolat 16f6a1a65d fix clippy 2022-07-29 12:24:49 +02:00
Alex Auvolat ad35b18bb1 Faster chunker 2022-07-29 12:24:49 +02:00
Alex Auvolat 49154a78d8 Update cargo.nix 2022-07-29 12:24:48 +02:00
Alex Auvolat ff4771c36a cargo fmt 2022-07-29 12:24:48 +02:00
Alex Auvolat 381eb9a5a1 Fix tests 2022-07-29 12:24:48 +02:00
Alex Auvolat 2cad656a03 More make clippy happy 2022-07-29 12:24:48 +02:00
Alex Auvolat 0176da3ad2 Make clippy happy 2022-07-29 12:24:48 +02:00
Alex Auvolat 40150527b8 Update cargo.nix 2022-07-29 12:24:48 +02:00
Alex Auvolat 2f111e6b3d Performance improvements:
- reduce contention on mutation_lock by having 256 of them
- better lmdb defaults
2022-07-29 12:24:48 +02:00
Alex Auvolat 1b2e1296eb Compute hashes on dedicated threads 2022-07-29 12:24:44 +02:00
Quentin Dufour a184f0d0b5 Migrate to nix-daemon builders 2022-07-29 08:37:33 +02:00
Quentin Dufour fcb04843f7 Run clippy in nix, leveraging nix caching ability 2022-07-26 18:27:52 +02:00
Quentin Dufour 5fb8584247 Refactor default.nix to follow Nix Flakes patterns 2022-07-26 18:27:52 +02:00
Quentin Dufour 96561c48a1 Bump Nix image to 22.05 2022-07-26 18:27:52 +02:00
Quentin Dufour a49d0ea19f Fix: compile aarch64+armv6 as static binaries 2022-07-26 18:27:51 +02:00
Quentin Dufour 9c9e483375 Put log-lines in nix.conf 2022-07-26 18:27:51 +02:00
Quentin Dufour 76cb34a0ae Fail if compiled binary is dynamic 2022-07-26 18:27:46 +02:00
Alex Auvolat ac03fa7937 Uniformize tracing::* imports (hopefully fixes 32-bit build) 2022-07-15 18:31:19 +02:00
Alex 4f38cadf6e Background task manager (#332)
- [x] New background worker trait
- [x] Adapt all current workers to use new API
- [x] Command to list currently running workers, and whether they are active, idle, or dead
- [x] Error reporting
- Optimizations
  - [x] Merkle updater: several items per iteration
  - [ ] Use `tokio::task::spawn_blocking` where appropriate so that CPU-intensive tasks don't block other things going on
- scrub:
  - [x] have only one worker with a channel to start/pause/cancel
  - [x] automatic scrub
  - [x] ability to view and change tranquility from CLI
  - [x] persistence of a few info
- [ ] Testing

Co-authored-by: Alex Auvolat <alex@adnab.me>
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/332
Co-authored-by: Alex <alex@adnab.me>
Co-committed-by: Alex <alex@adnab.me>
2022-07-08 13:30:26 +02:00
Alex Auvolat aab34bfe54 add delays in k2v test_items_and_indices 2022-07-08 10:41:57 +02:00
Alex fe3fa83de7 Publish k2v-client crate to crates.io (#337)
Co-authored-by: Alex Auvolat <alex@adnab.me>
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/337
Co-authored-by: Alex <alex@adnab.me>
Co-committed-by: Alex <alex@adnab.me>
2022-07-04 18:27:25 +02:00
Alex Auvolat b6d59ec19a Fix poll item when item didn't change 2022-07-04 14:00:02 +02:00
Alex 0850bac874 Add poll command to k2v-cli (#335)
Co-authored-by: Alex Auvolat <alex@adnab.me>
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/335
Co-authored-by: Alex <alex@adnab.me>
Co-committed-by: Alex <alex@adnab.me>
2022-07-04 12:45:32 +02:00
Alex b74b533b7b Fix typo 2022-06-29 11:50:51 +02:00
Alex 996f2a6d58 Slides for talk at IMT Atlantique / STACK on 2022-06-23 (#333)
Co-authored-by: Alex Auvolat <alex@adnab.me>
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/333
Co-authored-by: Alex <alex@adnab.me>
Co-committed-by: Alex <alex@adnab.me>
2022-06-23 14:28:40 +02:00
Alex 77e3fd6db2 improve internal item counter mechanisms and implement bucket quotas (#326)
- [x] Refactoring of internal counting API
- [x] Repair procedure for counters (it's an offline procedure!!!)
- [x] New counter for objects in buckets
- [x] Add quotas to buckets struct
- [x] Add CLI to manage bucket quotas
- [x] Add admin API to manage bucket quotas
- [x] Apply quotas by adding checks on put operations
- [x] Proof-read

Co-authored-by: Alex Auvolat <alex@adnab.me>
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/326
Co-authored-by: Alex <alex@adnab.me>
Co-committed-by: Alex <alex@adnab.me>
2022-06-15 20:20:28 +02:00
Quentin Dufour d544a0e0e0 Send CORS headers for all requests 2022-06-13 10:19:52 +02:00
Alex Auvolat 138e13071b Fix garage_db build on 32-bit systems 2022-06-09 14:55:20 +02:00
Alex b44d3fc796 Abstract database behind generic interface and implement alternative drivers (#322)
- [x] Design interface
- [x] Implement Sled backend
  - [x] Re-implement the SledCountedTree hack ~~on Sled backend~~ on all backends (i.e. over the abstraction)
- [x] Convert Garage code to use generic interface
- [x] Proof-read converted Garage code
- [ ] Test everything well
- [x] Implement sqlite backend
- [x] Implement LMDB backend
- [ ] (Implement Persy backend?)
- [ ] (Implement other backends? (like RocksDB, ...))
- [x] Implement backend choice in config file and garage server module
- [x] Add CLI for converting between DB formats
- Exploit the new interface to put more things in transactions
  - [x] `.updated()` trigger on Garage tables

Fix #284

**Bugs**

- [x] When exporting sqlite, trees iterate empty??
- [x] LMDB doesn't work

**Known issues for various back-ends**

- Sled:
  - Eats all my RAM and also all my disk space
  - `.len()` has to traverse the whole table
  - Is actually quite slow on some operations
  - And is actually pretty bad code...
- Sqlite:
  - Requires a lock to be taken on all operations. The lock is also taken when iterating on a table with `.iter()`, and the lock isn't released until the iterator is dropped. This means that we must be VERY carefull to not do anything else inside a `.iter()` loop or else we will have a deadlock! Most such cases have been eliminated from the Garage codebase, but there might still be some that remain. If your Garage-over-Sqlite seems to hang/freeze, this is the reason.
  - (adapter uses a bunch of unsafe code)
- Heed (LMDB):
  - Not suited for 32-bit machines as it has to map the whole DB in memory.
  - (adpater uses a tiny bit of unsafe code)

**My recommendation:** avoid 32-bit machines and use LMDB as much as possible.

**Converting databases** is actually quite easy. For example from Sled to LMDB:

```bash
cd src/db
cargo run --features cli --bin convert -- -i path/to/garage/meta/db -a sled -o path/to/garage/meta/db.lmdb -b lmdb
```

Then, just add this to your `config.toml`:

```toml
db_engine = "lmdb"
```

Co-authored-by: Alex Auvolat <alex@adnab.me>
Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/322
Co-authored-by: Alex <alex@adnab.me>
Co-committed-by: Alex <alex@adnab.me>
2022-06-08 10:01:44 +02:00
Simon C 7eed3ceda9 docs: Add Trafik reverse proxy documentation 2022-06-07 16:16:52 +02:00
Simon C 4b8f48f3c5 docs: Fix title level 2022-06-07 13:32:52 +02:00
Simon C 7d3b5585f1 docs: Add link to facilitate navigation in the documentation 2022-06-07 09:38:59 +02:00
Quentin Dufour a1abed0378 Remove useless MC_REGION env variable 2022-06-02 12:50:11 +02:00
215 changed files with 21340 additions and 5598 deletions
+20 -6
View File
@@ -19,9 +19,11 @@ steps:
- name: unit + func tests
image: nixpkgs/nix:nixos-22.05
environment:
GARAGE_TEST_INTEGRATION_EXE: result/bin/garage
GARAGE_TEST_INTEGRATION_EXE: result-bin/bin/garage
commands:
- nix-build --no-build-output --attr clippy.amd64 --argstr git_version ${DRONE_TAG:-$DRONE_COMMIT}
- nix-build --no-build-output --attr test.amd64
- ./result/bin/garage_db-*
- ./result/bin/garage_api-*
- ./result/bin/garage_model-*
- ./result/bin/garage_rpc-*
@@ -30,6 +32,7 @@ steps:
- ./result/bin/garage_web-*
- ./result/bin/garage-*
- ./result/bin/integration-*
- rm result
- name: integration tests
image: nixpkgs/nix:nixos-22.05
@@ -58,7 +61,7 @@ steps:
image: nixpkgs/nix:nixos-22.05
commands:
- nix-build --no-build-output --attr pkgs.amd64.release --argstr git_version ${DRONE_TAG:-$DRONE_COMMIT}
- nix-shell --attr rust --run "./script/not-dynamic.sh result/bin/garage"
- nix-shell --attr rust --run "./script/not-dynamic.sh result-bin/bin/garage"
- name: integration
image: nixpkgs/nix:nixos-22.05
@@ -109,7 +112,7 @@ steps:
image: nixpkgs/nix:nixos-22.05
commands:
- nix-build --no-build-output --attr pkgs.i386.release --argstr git_version ${DRONE_TAG:-$DRONE_COMMIT}
- nix-shell --attr rust --run "./script/not-dynamic.sh result/bin/garage"
- nix-shell --attr rust --run "./script/not-dynamic.sh result-bin/bin/garage"
- name: integration
image: nixpkgs/nix:nixos-22.05
@@ -159,7 +162,7 @@ steps:
image: nixpkgs/nix:nixos-22.05
commands:
- nix-build --no-build-output --attr pkgs.arm64.release --argstr git_version ${DRONE_TAG:-$DRONE_COMMIT}
- nix-shell --attr rust --run "./script/not-dynamic.sh result/bin/garage"
- nix-shell --attr rust --run "./script/not-dynamic.sh result-bin/bin/garage"
- name: push static binary
image: nixpkgs/nix:nixos-22.05
@@ -204,7 +207,7 @@ steps:
image: nixpkgs/nix:nixos-22.05
commands:
- nix-build --no-build-output --attr pkgs.arm.release --argstr git_version ${DRONE_TAG:-$DRONE_COMMIT}
- nix-shell --attr rust --run "./script/not-dynamic.sh result/bin/garage"
- nix-shell --attr rust --run "./script/not-dynamic.sh result-bin/bin/garage"
- name: push static binary
image: nixpkgs/nix:nixos-22.05
@@ -245,6 +248,17 @@ node:
nix-daemon: 1
steps:
- name: multiarch-docker
image: nixpkgs/nix:nixos-22.05
environment:
DOCKER_AUTH:
from_secret: docker_auth
HOME: "/root"
commands:
- mkdir -p /root/.docker
- echo $DOCKER_AUTH > /root/.docker/config.json
- export CONTAINER_TAG=${DRONE_TAG:-$DRONE_COMMIT}
- nix-shell --attr release --run "multiarch_docker"
- name: refresh-index
image: nixpkgs/nix:nixos-22.05
environment:
@@ -269,6 +283,6 @@ trigger:
---
kind: signature
hmac: fa1f98f327abf88486c0c54984287285a4b951efa3776af9dd33b4d782b50815
hmac: ac09a5a8c82502f67271f93afa1e1e21ce66383b8e24a6deb26b285cc1c378ba
...
Generated
+1019 -551
View File
File diff suppressed because it is too large Load Diff
+2595 -1839
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -1,5 +1,7 @@
[workspace]
resolver = "2"
members = [
"src/db",
"src/util",
"src/rpc",
"src/table",
+1 -1
View File
@@ -3,5 +3,5 @@ FROM scratch
ENV RUST_BACKTRACE=1
ENV RUST_LOG=garage=info
COPY result/bin/garage /
COPY result-bin/bin/garage /
CMD [ "/garage", "server"]
+19 -5
View File
@@ -1,13 +1,27 @@
.PHONY: doc all release shell
.PHONY: doc all release shell run1 run2 run3
all:
clear; cargo build --all-features
doc:
cd doc/book; mdbook build
clear; cargo build
release:
nix-build --arg release true
shell:
nix-shell
# ----
run1:
RUST_LOG=garage=debug ./target/debug/garage -c tmp/config1.toml server
run1rel:
RUST_LOG=garage=debug ./target/release/garage -c tmp/config1.toml server
run2:
RUST_LOG=garage=debug ./target/debug/garage -c tmp/config2.toml server
run2rel:
RUST_LOG=garage=debug ./target/release/garage -c tmp/config2.toml server
run3:
RUST_LOG=garage=debug ./target/debug/garage -c tmp/config3.toml server
run3rel:
RUST_LOG=garage=debug ./target/release/garage -c tmp/config3.toml server
+17 -11
View File
@@ -15,18 +15,24 @@ Garage [![Build Status](https://drone.deuxfleurs.fr/api/badges/Deuxfleurs/garage
]
</p>
Garage is a lightweight S3-compatible distributed object store, with the following goals:
Garage is an S3-compatible distributed object storage service
designed for self-hosting at a small-to-medium scale.
- As self-contained as possible
- Easy to set up
- Highly resilient to network failures, network latency, disk failures, sysadmin failures
- Relatively simple
- Made for multi-datacenter deployments
Garage is designed for storage clusters composed of nodes running
at different physical locations,
in order to easily provide a storage service that replicates data at these different
locations and stays available even when some servers are unreachable.
Garage also focuses on being lightweight, easy to operate, and highly resilient to
machine failures.
Non-goals include:
Garage is built by [Deuxfleurs](https://deuxfleurs.fr),
an experimental small-scale self hosted service provider,
which has been using it in production since its first release in 2020.
- Extremely high performance
- Complete implementation of the S3 API
- Erasure coding (our replication model is simply to copy the data as is on several nodes, in different datacenters if possible)
Learn more on our dedicated documentation pages:
Our main use case is to provide a distributed storage layer for small-scale self hosted services such as [Deuxfleurs](https://deuxfleurs.fr).
- [Goals and use cases](https://garagehq.deuxfleurs.fr/documentation/design/goals/)
- [Features](https://garagehq.deuxfleurs.fr/documentation/reference-manual/features/)
- [Quick start](https://garagehq.deuxfleurs.fr/documentation/quick-start/)
Garage is entirely free software released under the terms of the AGPLv3.
+34 -5
View File
@@ -5,13 +5,26 @@
with import ./nix/common.nix;
let
let
pkgs = import pkgsSrc { };
compile = import ./nix/compile.nix;
build_debug_and_release = (target: {
debug = (compile { inherit target git_version; release = false; }).workspace.garage { compileMode = "build"; };
release = (compile { inherit target git_version; release = true; }).workspace.garage { compileMode = "build"; };
debug = (compile {
inherit target git_version;
release = false;
}).workspace.garage {
compileMode = "build";
};
release = (compile {
inherit target git_version;
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);
@@ -25,9 +38,25 @@ in {
arm = build_debug_and_release "armv6l-unknown-linux-musleabihf";
};
test = {
amd64 = test (compile { inherit git_version; target = "x86_64-unknown-linux-musl"; });
amd64 = test (compile {
inherit git_version;
target = "x86_64-unknown-linux-musl";
features = [
"garage/bundled-libs"
"garage/k2v"
"garage/sled"
"garage/lmdb"
"garage/sqlite"
];
});
};
clippy = {
amd64 = (compile { inherit git_version; compiler = "clippy"; }).workspace.garage { compileMode = "build"; } ;
amd64 = (compile {
inherit git_version;
target = "x86_64-unknown-linux-musl";
compiler = "clippy";
}).workspace.garage {
compileMode = "build";
};
};
}
+1 -1
View File
@@ -3,7 +3,7 @@ title = "Websites (Hugo, Jekyll, Publii...)"
weight = 10
+++
Garage is also suitable to host static websites.
Garage is also suitable [to host static websites](@/documentation/cookbook/exposing-websites.md).
While they can be deployed with traditional CLI tools, some static website generators have integrated options to ease your workflow.
| Name | Status | Note |
+3 -1
View File
@@ -5,12 +5,14 @@ weight = 25
## Configuring a bucket for website access
There are two methods to expose buckets as website:
There are three methods to expose buckets as website:
1. using the PutBucketWebsite S3 API call, which is allowed for access keys that have the owner permission bit set
2. from the Garage CLI, by an adminstrator of the cluster
3. using the Garage administration API
The `PutBucketWebsite` API endpoint [is documented](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketWebsite.html) in the official AWS docs.
This endpoint can also be called [using `aws s3api`](https://docs.aws.amazon.com/cli/latest/reference/s3api/put-bucket-website.html) on the command line.
The website configuration supported by Garage is only a subset of the possibilities on Amazon S3: redirections are not supported, only the index document and error document can be specified.
+62 -26
View File
@@ -20,40 +20,76 @@ sudo apt-get update
sudo apt-get install build-essential
```
## Using source from `crates.io`
Garage's source code is published on `crates.io`, Rust's official package repository.
This means you can simply ask `cargo` to download and build this source code for you:
```bash
cargo install garage
```
That's all, `garage` should be in `$HOME/.cargo/bin`.
You can add this folder to your `$PATH` or copy the binary somewhere else on your system.
For instance:
```bash
sudo cp $HOME/.cargo/bin/garage /usr/local/bin/garage
```
## Using source from the Gitea repository
## Building from source from the Gitea repository
The primary location for Garage's source code is the
[Gitea repository](https://git.deuxfleurs.fr/Deuxfleurs/garage).
[Gitea repository](https://git.deuxfleurs.fr/Deuxfleurs/garage),
which contains all of the released versions as well as the code
for the developpement of the next version.
Clone the repository and build Garage with the following commands:
Clone the repository and enter it as follows:
```bash
git clone https://git.deuxfleurs.fr/Deuxfleurs/garage.git
cd garage
cargo build
```
Be careful, as this will make a debug build of Garage, which will be extremely slow!
To make a release build, invoke `cargo build --release` (this takes much longer).
If you wish to build a specific version of Garage, check out the corresponding tag. For instance:
The binaries built this way are found in `target/{debug,release}/garage`.
```bash
git tag # List available tags
git checkout v0.8.0 # Change v0.8.0 with the version you wish to build
```
Otherwise you will be building a developpement build from the `main` branch
that includes all of the changes to be released in the next version.
Be careful that such a build might be unstable or contain bugs,
and could be incompatible with nodes that run stable versions of Garage.
Finally, build Garage with the following command:
```bash
cargo build --release
```
The binary built this way can now be found in `target/release/garage`.
You may simply copy this binary to somewhere in your `$PATH` in order to
have the `garage` command available in your shell, for instance:
```bash
sudo cp target/release/garage /usr/local/bin/garage
```
If you are planning to develop Garage,
you might be interested in producing debug builds, which compile faster but run slower:
this can be done by removing the `--release` flag, and the resulting build can then
be found in `target/debug/garage`.
## List of available Cargo feature flags
Garage supports a number of compilation options in the form of Cargo feature flags,
which can be used to provide builds adapted to your system and your use case.
To produce a build with a given set of features, invoke the `cargo build` command
as follows:
```bash
# This will build the default feature set plus feature1, feature2 and feature3
cargo build --release --features feature1,feature2,feature3
# This will build ONLY feature1, feature2 and feature3
cargo build --release --no-default-features \
--features feature1,feature2,feature3
```
The following feature flags are available in v0.8.0:
| Feature flag | Enabled | Description |
| ------------ | ------- | ----------- |
| `bundled-libs` | *by default* | Use bundled version of sqlite3, zstd, lmdb and libsodium |
| `system-libs` | optional | Use system version of sqlite3, zstd, lmdb and libsodium<br>if available (exclusive with `bundled-libs`, build using<br>`cargo build --no-default-features --features system-libs`) |
| `k2v` | optional | Enable the experimental K2V API (if used, all nodes on your<br>Garage cluster must have it enabled as well) |
| `kubernetes-discovery` | optional | Enable automatic registration and discovery<br>of cluster nodes through the Kubernetes API |
| `metrics` | *by default* | Enable collection of metrics in Prometheus format on the admin API |
| `telemetry-otlp` | optional | Enable collection of execution traces using OpenTelemetry |
| `sled` | *by default* | Enable using Sled to store Garage's metadata |
| `lmdb` | optional | Enable using LMDB to store Garage's metadata |
| `sqlite` | optional | Enable using Sqlite3 to store Garage's metadata |
+87
View File
@@ -0,0 +1,87 @@
+++
title = "Deploying on Kubernetes"
weight = 32
+++
Garage can also be deployed on a kubernetes cluster via helm chart.
## Deploying
Firstly clone the repository:
```bash
git clone https://git.deuxfleurs.fr/Deuxfleurs/garage
cd garage/scripts/helm
```
Deploy with default options:
```bash
helm install --create-namespace --namespace garage garage ./garage
```
Or deploy with custom values:
```bash
helm install --create-namespace --namespace garage garage ./garage -f values.override.yaml
```
After deploying, cluster layout must be configured manually as described in [Creating a cluster layout](@/documentation/quick-start/_index.md#creating-a-cluster-layout). Use the following command to access garage CLI:
```bash
kubectl exec --stdin --tty -n garage garage-0 -- ./garage status
```
## Overriding default values
All possible configuration values can be found with:
```bash
helm show values ./garage
```
This is an example `values.overrride.yaml` for deploying in a microk8s cluster with a https s3 api ingress route:
```yaml
garage:
# Use only 2 replicas per object
replicationMode: "2"
# Start 4 instances (StatefulSets) of garage
replicaCount: 4
# Override default storage class and size
persistence:
meta:
storageClass: "openebs-hostpath"
size: 100Mi
data:
storageClass: "openebs-hostpath"
size: 1Gi
ingress:
s3:
api:
enabled: true
className: "public"
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/proxy-body-size: 500m
hosts:
- host: s3-api.my-domain.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: garage-ingress-cert
hosts:
- s3-api.my-domain.com
```
## Removing
```bash
helm delete --namespace garage garage
```
Note that this will leave behind custom CRD `garagenodes.deuxfleurs.fr`, which must be removed manually if desired.
+6 -6
View File
@@ -51,15 +51,15 @@ to store 2 TB of data in total.
## Get a Docker image
Our docker image is currently named `dxflrs/amd64_garage` and is stored on the [Docker Hub](https://hub.docker.com/r/dxflrs/amd64_garage/tags?page=1&ordering=last_updated).
We encourage you to use a fixed tag (eg. `v0.4.0`) and not the `latest` tag.
For this example, we will use the latest published version at the time of the writing which is `v0.4.0` but it's up to you
to check [the most recent versions on the Docker Hub](https://hub.docker.com/r/dxflrs/amd64_garage/tags?page=1&ordering=last_updated).
Our docker image is currently named `dxflrs/garage` and is stored on the [Docker Hub](https://hub.docker.com/r/dxflrs/garage/tags?page=1&ordering=last_updated).
We encourage you to use a fixed tag (eg. `v0.8.0`) and not the `latest` tag.
For this example, we will use the latest published version at the time of the writing which is `v0.8.0` but it's up to you
to check [the most recent versions on the Docker Hub](https://hub.docker.com/r/dxflrs/garage/tags?page=1&ordering=last_updated).
For example:
```
sudo docker pull dxflrs/amd64_garage:v0.4.0
sudo docker pull dxflrs/garage:v0.8.0
```
## Deploying and configuring Garage
@@ -125,7 +125,7 @@ docker run \
-v /etc/garage.toml:/etc/garage.toml \
-v /var/lib/garage/meta:/var/lib/garage/meta \
-v /var/lib/garage/data:/var/lib/garage/data \
lxpz/garage_amd64:v0.4.0
dxflrs/garage:v0.8.0
```
It should be restarted automatically at each reboot.
+162 -3
View File
@@ -100,7 +100,7 @@ server {
}
```
## Exposing the web endpoint
### Exposing the web endpoint
To better understand the logic involved, you can refer to the [Exposing buckets as websites](/cookbook/exposing_websites.html) section.
Otherwise, the configuration is very similar to the S3 endpoint.
@@ -140,6 +140,165 @@ server {
@TODO
## Traefik
## Traefik v2
We will see in this part how to set up a reverse proxy with [Traefik](https://docs.traefik.io/).
Here is [a basic configuration file](https://doc.traefik.io/traefik/https/acme/#configuration-examples):
```toml
[entryPoints]
[entryPoints.web]
address = ":80"
[entryPoints.websecure]
address = ":443"
[certificatesResolvers.myresolver.acme]
email = "your-email@example.com"
storage = "acme.json"
[certificatesResolvers.myresolver.acme.httpChallenge]
# used during the challenge
entryPoint = "web"
```
### Add Garage service
To add Garage on Traefik you should declare a new service using its IP address (or hostname) and port:
```toml
[http.services]
[http.services.my_garage_service.loadBalancer]
[[http.services.my_garage_service.loadBalancer.servers]]
url = "http://xxx.xxx.xxx.xxx"
port = 3900
```
It's possible to declare multiple Garage servers as back-ends:
```toml
[http.services]
[[http.services.my_garage_service.loadBalancer.servers]]
url = "http://xxx.xxx.xxx.xxx"
port = 3900
[[http.services.my_garage_service.loadBalancer.servers]]
url = "http://yyy.yyy.yyy.yyy"
port = 3900
[[http.services.my_garage_service.loadBalancer.servers]]
url = "http://zzz.zzz.zzz.zzz"
port = 3900
```
Traefik can remove unhealthy servers automatically with [a health check configuration](https://doc.traefik.io/traefik/routing/services/#health-check):
```
[http.services]
[http.services.my_garage_service.loadBalancer]
[http.services.my_garage_service.loadBalancer.healthCheck]
path = "/"
interval = "60s"
timeout = "5s"
```
### Adding a website
To add a new website, add the following declaration to your Traefik configuration file:
```toml
[http.routers]
[http.routers.my_website]
rule = "Host(`yoururl.example.org`)"
service = "my_garage_service"
entryPoints = ["web"]
```
Enable HTTPS access to your website with the following configuration section ([documentation](https://doc.traefik.io/traefik/https/overview/)):
```toml
...
entryPoints = ["websecure"]
[http.routers.my_website.tls]
certResolver = "myresolver"
...
```
### Adding gzip compression
Add the following configuration section [to compress response](https://doc.traefik.io/traefik/middlewares/http/compress/) using [gzip](https://developer.mozilla.org/en-US/docs/Glossary/GZip_compression) before sending them to the client:
```toml
[http.routers]
[http.routers.my_website]
...
middlewares = ["gzip_compress"]
...
[http.middlewares]
[http.middlewares.gzip_compress.compress]
```
### Add caching response
Traefik's caching middleware is only available on [entreprise version](https://doc.traefik.io/traefik-enterprise/middlewares/http-cache/), however the freely-available [Souin plugin](https://github.com/darkweak/souin#tr%C3%A6fik-container) can also do the job. (section to be completed)
### Complete example
```toml
[entryPoints]
[entryPoints.web]
address = ":80"
[entryPoints.websecure]
address = ":443"
[certificatesResolvers.myresolver.acme]
email = "your-email@example.com"
storage = "acme.json"
[certificatesResolvers.myresolver.acme.httpChallenge]
# used during the challenge
entryPoint = "web"
[http.routers]
[http.routers.my_website]
rule = "Host(`yoururl.example.org`)"
service = "my_garage_service"
middlewares = ["gzip_compress"]
entryPoints = ["websecure"]
[http.services]
[http.services.my_garage_service.loadBalancer]
[http.services.my_garage_service.loadBalancer.healthCheck]
path = "/"
interval = "60s"
timeout = "5s"
[[http.services.my_garage_service.loadBalancer.servers]]
url = "http://xxx.xxx.xxx.xxx"
[[http.services.my_garage_service.loadBalancer.servers]]
url = "http://yyy.yyy.yyy.yyy"
[[http.services.my_garage_service.loadBalancer.servers]]
url = "http://zzz.zzz.zzz.zzz"
[http.middlewares]
[http.middlewares.gzip_compress.compress]
```
## Caddy
Your Caddy configuration can be as simple as:
```caddy
s3.garage.tld, *.s3.garage.tld {
reverse_proxy localhost:3900 192.168.1.2:3900 example.tld:3900
}
*.web.garage.tld {
reverse_proxy localhost:3902 192.168.1.2:3900 example.tld:3900
}
admin.garage.tld {
reverse_proxy localhost:3903
}
```
But at the same time, the `reverse_proxy` is very flexible.
For a production deployment, you should [read its documentation](https://caddyserver.com/docs/caddyfile/directives/reverse_proxy) as it supports features like DNS discovery of upstreams, load balancing with checks, streaming parameters, etc.
@TODO
+1 -1
View File
@@ -1,6 +1,6 @@
+++
title = "Benchmarks"
weight = 10
weight = 40
+++
With Garage, we wanted to build a software defined storage service that follow the [KISS principle](https://en.wikipedia.org/wiki/KISS_principle),
+3 -3
View File
@@ -1,23 +1,23 @@
+++
title = "Goals and use cases"
weight = 5
weight = 10
+++
## Goals and non-goals
Garage is a lightweight geo-distributed data store that implements the
[Amazon S3](https://docs.aws.amazon.com/AmazonS3/latest/API/Welcome.html)
object storage protocole. It enables applications to store large blobs such
object storage protocol. It enables applications to store large blobs such
as pictures, video, images, documents, etc., in a redundant multi-node
setting. S3 is versatile enough to also be used to publish a static
website.
Garage is an opinionated object storage solutoin, we focus on the following **desirable properties**:
- **Internet enabled**: made for multi-sites (eg. datacenters, offices, households, etc.) interconnected through regular Internet connections.
- **Self-contained & lightweight**: works everywhere and integrates well in existing environments to target [hyperconverged infrastructures](https://en.wikipedia.org/wiki/Hyper-converged_infrastructure).
- **Highly resilient**: highly resilient to network failures, network latency, disk failures, sysadmin failures.
- **Simple**: simple to understand, simple to operate, simple to debug.
- **Internet enabled**: made for multi-sites (eg. datacenters, offices, households, etc.) interconnected through regular Internet connections.
We also noted that the pursuit of some other goals are detrimental to our initial goals.
The following has been identified as **non-goals** (if these points matter to you, you should not use Garage):
+43
View File
@@ -20,6 +20,49 @@ In the meantime, you can find some information at the following links:
- [an old design draft](@/documentation/working-documents/design-draft.md)
## Request routing logic
Data retrieval requests to Garage endpoints (S3 API and websites) are resolved
to an individual object in a bucket. Since objects are replicated to multiple nodes
Garage must ensure consistency before answering the request.
### Using quorum to ensure consistency
Garage ensures consistency by attempting to establish a quorum with the
data nodes responsible for the object. When a majority of the data nodes
have provided metadata on a object Garage can then answer the request.
When a request arrives Garage will, assuming the recommended 3 replicas, perform the following actions:
- Make a request to the two preferred nodes for object metadata
- Try the third node if one of the two initial requests fail
- Check that the metadata from at least 2 nodes match
- Check that the object hasn't been marked deleted
- Answer the request with inline data from metadata if object is small enough
- Or get data blocks from the preferred nodes and answer using the assembled object
Garage dynamically determines which nodes to query based on health, preference, and
which nodes actually host a given data. Garage has no concept of "primary" so any
healthy node with the data can be used as long as a quorum is reached for the metadata.
### Node health
Garage keeps a TCP session open to each node in the cluster and periodically pings them. If a connection
cannot be established, or a node fails to answer a number of pings, the target node is marked as failed.
Failed nodes are not used for quorum or other internal requests.
### Node preference
Garage prioritizes which nodes to query according to a few criteria:
- A node always prefers itself if it can answer the request
- Then the node prioritizes nodes in the same zone
- Finally the nodes with the lowest latency are prioritized
For further reading on the cluster structure look at the [gateway](@/documentation/cookbook/gateways.md)
and [cluster layout management](@/documentation/reference-manual/layout.md) pages.
## Garbage collection
A faulty garbage collection procedure has been the cause of
+1 -1
View File
@@ -1,6 +1,6 @@
+++
title = "Related work"
weight = 15
weight = 50
+++
## Context
+9 -10
View File
@@ -9,6 +9,15 @@ Let's start your Garage journey!
In this chapter, we explain how to deploy Garage as a single-node server
and how to interact with it.
## What is Garage?
Before jumping in, you might be interested in reading the following pages:
- [Goals and use cases](@/documentation/design/goals.md)
- [List of features](@/documentation/reference-manual/features.md)
## Scope of this tutorial
Our goal is to introduce you to Garage's workflows.
Following this guide is recommended before moving on to
[configuring a multi-node cluster](@/documentation/cookbook/real-world.md).
@@ -249,16 +258,6 @@ mc alias set \
--api S3v4
```
You must also add an environment variable to your configuration to
inform MinIO of our region (`garage` by default, corresponding to the `s3_region` parameter
in the configuration file).
The best way is to add the following snippet to your `$HOME/.bash_profile`
or `$HOME/.bashrc` file:
```bash
export MC_REGION=garage
```
### Use `mc`
You can not list buckets from `mc` currently.
+89 -71
View File
@@ -1,6 +1,6 @@
+++
title = "Administration API"
weight = 16
weight = 60
+++
The Garage administration API is accessible through a dedicated server whose
@@ -134,8 +134,8 @@ Example request body:
```json
[
"ec79480e0ce52ae26fd00c9da684e4fa56658d9c64cdcecb094e936de0bfe71f@10.0.0.11:3901",
"4a6ae5a1d0d33bf895f5bb4f0a418b7dc94c47c0dd2eb108d1158f3c8f60b0ff@10.0.0.12:3901"
"ec79480e0ce52ae26fd00c9da684e4fa56658d9c64cdcecb094e936de0bfe71f@10.0.0.11:3901",
"4a6ae5a1d0d33bf895f5bb4f0a418b7dc94c47c0dd2eb108d1158f3c8f60b0ff@10.0.0.12:3901"
]
```
@@ -145,14 +145,14 @@ Example response:
```json
[
{
"success": true,
"error": null
},
{
"success": false,
"error": "Handshake error"
}
{
"success": true,
"error": null
},
{
"success": false,
"error": "Handshake error"
}
]
```
@@ -301,7 +301,7 @@ Request body format:
```json
{
"name": "NameOfMyKey"
"name": "NameOfMyKey"
}
```
@@ -313,9 +313,9 @@ Request body format:
```json
{
"accessKeyId": "GK31c2f218a2e44f485b94239e",
"secretAccessKey": "b892c0665f0ada8a4755dae98baa3b133590e11dae3bcc1f9d769d67f16c3835",
"name": "NameOfMyKey"
"accessKeyId": "GK31c2f218a2e44f485b94239e",
"secretAccessKey": "b892c0665f0ada8a4755dae98baa3b133590e11dae3bcc1f9d769d67f16c3835",
"name": "NameOfMyKey"
}
```
@@ -403,11 +403,11 @@ Request body format:
```json
{
"name": "NameOfMyKey",
"allow": {
"createBucket": true,
},
"deny": {}
"name": "NameOfMyKey",
"allow": {
"createBucket": true,
},
"deny": {}
}
```
@@ -473,24 +473,31 @@ Example response:
```json
{
"id": "e6a14cd6a27f48684579ec6b381c078ab11697e6bc8513b72b2f5307e25fff9b",
"globalAliases": [
"alex"
],
"keys": [
{
"accessKeyId": "GK31c2f218a2e44f485b94239e",
"name": "alex",
"permissions": {
"read": true,
"write": true,
"owner": true
},
"bucketLocalAliases": [
"test"
]
}
]
"id": "afa8f0a22b40b1247ccd0affb869b0af5cff980924a20e4b5e0720a44deb8d39",
"globalAliases": [],
"websiteAccess": false,
"websiteConfig": null,
"keys": [
{
"accessKeyId": "GK31c2f218a2e44f485b94239e",
"name": "Imported key",
"permissions": {
"read": true,
"write": true,
"owner": true
},
"bucketLocalAliases": [
"debug"
]
}
],
"objects": 14827,
"bytes": 13189855625,
"unfinshedUploads": 0,
"quotas": {
"maxSize": null,
"maxObjects": null
}
}
```
@@ -502,7 +509,7 @@ Request body format:
```json
{
"globalAlias": "NameOfMyBucket"
"globalAlias": "NameOfMyBucket"
}
```
@@ -510,15 +517,15 @@ OR
```json
{
"localAlias": {
"accessKeyId": "GK31c2f218a2e44f485b94239e",
"alias": "NameOfMyBucket",
"allow": {
"read": true,
"write": true,
"owner": false
}
}
"localAlias": {
"accessKeyId": "GK31c2f218a2e44f485b94239e",
"alias": "NameOfMyBucket",
"allow": {
"read": true,
"write": true,
"owner": false
}
}
}
```
@@ -540,26 +547,37 @@ Deletes a storage bucket. A bucket cannot be deleted if it is not empty.
Warning: this will delete all aliases associated with the bucket!
#### PutBucketWebsite `PUT /v0/bucket/website?id=<bucket id>`
#### UpdateBucket `PUT /v0/bucket?id=<bucket id>`
Sets the website configuration for a bucket (this also enables website access for this bucket).
Updates configuration of the given bucket.
Request body format:
```json
{
"indexDocument": "index.html",
"errorDocument": "404.html"
"websiteAccess": {
"enabled": true,
"indexDocument": "index.html",
"errorDocument": "404.html"
},
"quotas": {
"maxSize": 19029801,
"maxObjects": null,
}
}
```
The field `errorDocument` is optional, if no error document is set a generic error message is displayed when errors happen.
All fields (`websiteAccess` and `quotas`) are optionnal.
If they are present, the corresponding modifications are applied to the bucket, otherwise nothing is changed.
In `websiteAccess`: if `enabled` is `true`, `indexDocument` must be specified.
The field `errorDocument` is optional, if no error document is set a generic
error message is displayed when errors happen. Conversely, if `enabled` is
`false`, neither `indexDocument` nor `errorDocument` must be specified.
#### DeleteBucketWebsite `DELETE /v0/bucket/website?id=<bucket id>`
Deletes the website configuration for a bucket (disables website access for this bucket).
In `quotas`: new values of `maxSize` and `maxObjects` must both be specified, or set to `null`
to remove the quotas. An absent value will be considered the same as a `null`. It is not possible
to change only one of the two quotas.
### Operations on permissions for keys on buckets
@@ -571,13 +589,13 @@ Request body format:
```json
{
"bucketId": "e6a14cd6a27f48684579ec6b381c078ab11697e6bc8513b72b2f5307e25fff9b",
"accessKeyId": "GK31c2f218a2e44f485b94239e",
"permissions": {
"read": true,
"write": true,
"owner": true
},
"bucketId": "e6a14cd6a27f48684579ec6b381c078ab11697e6bc8513b72b2f5307e25fff9b",
"accessKeyId": "GK31c2f218a2e44f485b94239e",
"permissions": {
"read": true,
"write": true,
"owner": true
},
}
```
@@ -592,13 +610,13 @@ Request body format:
```json
{
"bucketId": "e6a14cd6a27f48684579ec6b381c078ab11697e6bc8513b72b2f5307e25fff9b",
"accessKeyId": "GK31c2f218a2e44f485b94239e",
"permissions": {
"read": false,
"write": false,
"owner": true
},
"bucketId": "e6a14cd6a27f48684579ec6b381c078ab11697e6bc8513b72b2f5307e25fff9b",
"accessKeyId": "GK31c2f218a2e44f485b94239e",
"permissions": {
"read": false,
"write": false,
"owner": true
},
}
```
+1 -1
View File
@@ -1,6 +1,6 @@
+++
title = "Garage CLI"
weight = 15
weight = 30
+++
The Garage CLI is mostly self-documented. Make use of the `help` subcommand
+108 -44
View File
@@ -1,6 +1,6 @@
+++
title = "Configuration file format"
weight = 5
weight = 20
+++
Here is an example `garage.toml` configuration file that illustrates all of the possible options:
@@ -9,8 +9,12 @@ Here is an example `garage.toml` configuration file that illustrates all of the
metadata_dir = "/var/lib/garage/meta"
data_dir = "/var/lib/garage/data"
db_engine = "lmdb"
block_size = 1048576
block_manager_background_tranquility = 2
sled_cache_capacity = 134217728
sled_flush_every_ms = 2000
replication_mode = "3"
@@ -27,15 +31,20 @@ bootstrap_peers = [
"212fd62eeaca72c122b45a7f4fa0f55e012aa5e24ac384a72a3016413fa724ff@[fc00:F::1]:3901",
]
consul_host = "consul.service"
consul_service_name = "garage-daemon"
kubernetes_namespace = "garage"
kubernetes_service_name = "garage-daemon"
kubernetes_skip_crd = false
[consul_discovery]
consul_http_addr = "http://127.0.0.1:8500"
service_name = "garage-daemon"
ca_cert = "/etc/consul/consul-ca.crt"
client_cert = "/etc/consul/consul-client.crt"
client_key = "/etc/consul/consul-key.crt"
tls_skip_verify = false
[kubernetes_discovery]
namespace = "garage"
service_name = "garage-daemon"
skip_crd = false
sled_cache_capacity = 134217728
sled_flush_every_ms = 2000
[s3_api]
api_bind_addr = "[::]:3900"
@@ -72,6 +81,47 @@ 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).
### `db_engine` (since `v0.8.0`)
By default, Garage uses the Sled embedded database library
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 traditionnal 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.
### `block_size`
Garage splits stored objects in consecutive chunks of size `block_size`
@@ -87,16 +137,20 @@ 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.
### `block_manager_background_tranquility`
### `sled_cache_capacity`
This parameter tunes the activity of the background worker responsible for
resyncing data blocks between nodes. The higher the tranquility value is set,
the more the background worker will wait between iterations, meaning the load
on the system (including network usage between nodes) will be reduced. The
minimal value for this parameter is `0`, where the background worker will
allways work at maximal throughput to resynchronize blocks. The default value
is `2`, where the background worker will try to spend at most 1/3 of its time
working, and 2/3 sleeping in order to reduce system load.
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.
### `replication_mode`
@@ -245,48 +299,58 @@ 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.
### `consul_host` and `consul_service_name`
## The `[consul_discovery]` section
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
reached by other nodes of the cluster, which should be set in `rpc_public_addr`.
The `consul_host` parameter should be set to the hostname of the Consul server,
and `consul_service_name` should be set to the service name under which Garage's
### `consul_http_addr` and `service_name`
The `consul_http_addr` parameter should be set to the full HTTP(S) address of the Consul server.
### `service_name`
`service_name` should be set to the service name under which Garage's
RPC ports are announced.
Garage does not yet support talking to Consul over TLS.
### `client_cert`, `client_key`
### `kubernetes_namespace`, `kubernetes_service_name` and `kubernetes_skip_crd`
TLS client certificate and client key to use when communicating with Consul over TLS. Both are mandatory when doing so.
### `ca_cert`
TLS CA certificate to use when communicating with Consul over TLS.
### `tls_skip_verify`
Skip server hostname verification in TLS handshake.
`ca_cert` is ignored when this is set.
## The `[kubernetes_discovery]` section
Garage supports discovering other nodes of the cluster using kubernetes custom
resources. For this to work `kubernetes_namespace` and `kubernetes_service_name`
need to be configured.
resources. For this to work, a `[kubernetes_discovery]` section must be present
with at least the `namespace` and `service_name` parameters.
`kubernetes_namespace` sets the namespace in which the custom resources are
configured. `kubernetes_service_name` is added as a label to these resources to
### `namespace`
`namespace` sets the namespace in which the custom resources are
configured.
### `service_name`
`service_name` is added as a label to the advertised resources to
filter them, to allow for multiple deployments in a single namespace.
`kubernetes_skip_crd` can be set to true to disable the automatic creation and
### `skip_crd`
`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
manually.
### `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.
## The `[s3_api]` section
+125
View File
@@ -0,0 +1,125 @@
+++
title = "List of Garage features"
weight = 10
+++
### S3 API
The main goal of Garage is to provide an object storage service that is compatible with the
[S3 API](https://docs.aws.amazon.com/AmazonS3/latest/API/Welcome.html) from Amazon Web Services.
We try to adhere as strictly as possible to the semantics of the API as implemented by Amazon
and other vendors such as Minio or CEPH.
Of course Garage does not implement the full span of API endpoints that AWS S3 does;
the exact list of S3 features implemented by Garage can be found [on our S3 compatibility page](@/documentation/reference-manual/s3-compatibility.md).
### Geo-distribution
Garage allows you to store copies of your data in multiple geographical locations in order to maximize resilience
to adverse events, such as network/power outages or hardware failures.
This allows Garage to run very well even at home, using consumer-grade Internet connectivity
(such as FTTH) and power, as long as cluster nodes can be spawned at several physical locations.
Garage exploits knowledge of the capacity and physical location of each storage node to design
a storage plan that best exploits the available storage capacity while satisfying the geo-distributed replication constraint.
To learn more about geo-distributed Garage clusters,
read our documentation on [setting up a real-world deployment](@/documentation/cookbook/real-world.md).
### Standalone/self-contained
Garage is extremely simple to deploy, and does not depend on any external service to run.
This makes setting up and administering storage clusters, we hope, as easy as it could be.
### Flexible topology
A Garage cluster can very easily evolve over time, as storage nodes are added or removed.
Garage will automatically rebalance data between nodes as needed to ensure the desired number of copies.
Read about cluster layout management [here](@/documentation/reference-manual/layout.md).
### No RAFT slowing you down
It might seem strange to tout the absence of something as a desirable feature,
but this is in fact a very important point! Garage does not use RAFT or another
consensus algorithm internally to order incoming requests: this means that all requests
directed to a Garage cluster can be handled independently of one another instead
of going through a central bottleneck (the leader node).
As a consequence, requests can be handled much faster, even in cases where latency
between cluster nodes is important (see our [benchmarks](@/documentation/design/benchmarks/index.md) for data on this).
This is particularly usefull when nodes are far from one another and talk to one other through standard Internet connections.
### Several replication modes
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.
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).
### Web server for static websites
A storage bucket can easily be configured to be served directly by Garage as a static web site.
Domain names for multiple websites directly map to bucket names, making it easy to build
a platform for your users to autonomously build and host their websites over Garage.
Surprisingly, none of the other alternative S3 implementations we surveyed (such as Minio
or CEPH) support publishing static websites from S3 buckets, a feature that is however
directly inherited from S3 on AWS.
Read more on our [dedicated documentation page](@/documentation/cookbook/exposing-websites.md).
### Bucket names as aliases
In Garage, a bucket may have several names, known as aliases.
Aliases can easily be added and removed on demand:
this allows to easily rename buckets if needed
without having to copy all of their content, something that cannot be done on AWS.
For buckets served as static websites, having multiple aliases for a bucket can allow
exposing the same content under different domain names.
Garage also supports bucket aliases which are local to a single user:
this allows different users to have different buckets with the same name, thus avoiding naming collisions.
This can be helpfull for instance if you want to write an application that creates per-user buckets with always the same name.
This feature is totally invisible to S3 clients and does not break compatibility with AWS.
### Cluster administration API
Garage provides a fully-fledged REST API to administer your cluster programatically.
Functionnality included in the admin API include: setting up and monitoring
cluster nodes, managing access credentials, and managing storage buckets and bucket aliases.
A full reference of the administration API is available [here](@/documentation/reference-manual/admin-api.md).
### Metrics and traces
Garage makes some internal metrics available in the Prometheus data format,
which allows you to build interactive dashboards to visualize the load and internal state of your storage cluster.
For developpers and performance-savvy administrators,
Garage also supports exporting traces of what it does internally in OpenTelemetry format.
This allows to monitor the time spent at various steps of the processing of requests,
in order to detect potential performance bottlenecks.
### Kubernetes and Nomad integrations
Garage can automatically discover other nodes in the cluster thanks to integration
with orchestrators such as Kubernetes and Nomad (when used with Consul).
This eases the configuration of your cluster as it removes one step where nodes need
to be manually connected to one another.
### Support for changing IP addresses
As long as all of your nodes don't change their IP address at the same time,
Garage should be able to tolerate nodes with changing/dynamic IP addresses,
as nodes will regularly exchange the IP addresses of their peers and try to
reconnect using newer addresses when existing connections are broken.
### K2V API (experimental)
As part of an ongoing research project, Garage can expose an experimental key/value storage API called K2V.
K2V is made for the storage and retrieval of many small key/value pairs that need to be processed in bulk.
This completes the S3 API with an alternative that can be used to easily store and access metadata
related to objects stored in an S3 bucket.
In the context of our research project, [Aérogramme](https://aerogramme.deuxfleurs.fr),
K2V is used to provide metadata and log storage for operations on encrypted e-mail storage.
Learn more on the specification of K2V [here](https://git.deuxfleurs.fr/Deuxfleurs/garage/src/branch/k2v/doc/drafts/k2v-spec.md)
and on how to enable it in Garage [here](@/documentation/reference-manual/k2v.md).
+1 -1
View File
@@ -1,6 +1,6 @@
+++
title = "K2V"
weight = 30
weight = 70
+++
Starting with version 0.7.2, Garage introduces an optionnal feature, K2V,
+1 -1
View File
@@ -1,6 +1,6 @@
+++
title = "Cluster layout management"
weight = 10
weight = 50
+++
The cluster layout in Garage is a table that assigns to each node a role in
-45
View File
@@ -1,45 +0,0 @@
+++
title = "Request routing logic"
weight = 10
+++
Data retrieval requests to Garage endpoints (S3 API and websites) are resolved
to an individual object in a bucket. Since objects are replicated to multiple nodes
Garage must ensure consistency before answering the request.
## Using quorum to ensure consistency
Garage ensures consistency by attempting to establish a quorum with the
data nodes responsible for the object. When a majority of the data nodes
have provided metadata on a object Garage can then answer the request.
When a request arrives Garage will, assuming the recommended 3 replicas, perform the following actions:
- Make a request to the two preferred nodes for object metadata
- Try the third node if one of the two initial requests fail
- Check that the metadata from at least 2 nodes match
- Check that the object hasn't been marked deleted
- Answer the request with inline data from metadata if object is small enough
- Or get data blocks from the preferred nodes and answer using the assembled object
Garage dynamically determines which nodes to query based on health, preference, and
which nodes actually host a given data. Garage has no concept of "primary" so any
healthy node with the data can be used as long as a quorum is reached for the metadata.
## Node health
Garage keeps a TCP session open to each node in the cluster and periodically pings them. If a connection
cannot be established, or a node fails to answer a number of pings, the target node is marked as failed.
Failed nodes are not used for quorum or other internal requests.
## Node preference
Garage prioritizes which nodes to query according to a few criteria:
- A node always prefers itself if it can answer the request
- Then the node prioritizes nodes in the same zone
- Finally the nodes with the lowest latency are prioritized
For further reading on the cluster structure look at the [gateway](@/documentation/cookbook/gateways.md)
and [cluster layout management](@/documentation/reference-manual/layout.md) pages.
@@ -1,6 +1,6 @@
+++
title = "S3 Compatibility status"
weight = 20
weight = 40
+++
## DISCLAIMER
@@ -148,7 +148,7 @@ Please open an issue if you have a use case for replication.
| [PutBucketReplication](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketReplication.html) | ❌ Missing | ❌| ⚠ | ❌| ❌|
*Note: Ceph documentation briefly says that Ceph supports
[replication though the S3 API](https://docs.ceph.com/en/latest/radosgw/multisite-sync-policy/#s3-replication-api)
[replication through the S3 API](https://docs.ceph.com/en/latest/radosgw/multisite-sync-policy/#s3-replication-api)
but with some limitations.
Additionaly, replication endpoints are not documented in the S3 compatibility page so I don't know what kind of support we can expect.*
+2 -2
View File
@@ -1,6 +1,6 @@
+++
title = "Design draft"
weight = 25
title = "Design draft (obsolete)"
weight = 50
+++
**WARNING: this documentation is a design draft which was written before Garage's actual implementation.
+2 -2
View File
@@ -1,6 +1,6 @@
+++
title = "Load balancing data"
weight = 10
title = "Load balancing data (obsolete)"
weight = 60
+++
**This is being yet improved in release 0.5. The working document has not been updated yet, it still only applies to Garage 0.2 through 0.4.**
+1 -1
View File
@@ -16,7 +16,7 @@ The migration steps are as follows:
1. Do `garage repair --all-nodes --yes tables` and `garage repair --all-nodes --yes blocks`,
check the logs and check that all data seems to be synced correctly between
nodes. If you have time, do additional checks (`scrub`, `block_refs`, etc.)
2. Disable api and web access. Garage does not support disabling
2. Disable API and web access. Garage does not support disabling
these endpoints but you can change the port number or stop your reverse
proxy for instance.
3. Check once again that your cluster is healty. Run again `garage repair --all-nodes --yes tables` which is quick.
@@ -0,0 +1,34 @@
+++
title = "Migrating from 0.7 to 0.8"
weight = 13
+++
**This guide explains how to migrate to 0.8 if you have an existing 0.7 cluster.
We don't recommend trying to migrate to 0.8 directly from 0.6 or older.**
**We make no guarantee that this migration will work perfectly:
back up all your data before attempting it!**
Garage v0.8 introduces new data tables that allow the counting of objects in buckets in order to implement bucket quotas.
A manual migration step is required to first count objects in Garage buckets and populate these tables with accurate data.
The migration steps are as follows:
1. Disable API and web access. Garage v0.7 does not support disabling
these endpoints but you can change the port number or stop your reverse proxy for instance.
2. Do `garage repair --all-nodes --yes tables` and `garage repair --all-nodes --yes blocks`,
check the logs and check that all data seems to be synced correctly between
nodes. If you have time, do additional checks (`scrub`, `block_refs`, etc.)
3. Check that queues are empty: run `garage stats` to query them or inspect metrics in the Grafana dashboard.
4. Turn off Garage v0.7
5. **Backup the metadata folder of all your nodes!** For instance, use the following command
if your metadata directory is `/var/lib/garage/meta`: `cd /var/lib/garage ; tar -acf meta-v0.7.tar.zst meta/`
6. Install Garage v0.8
7. **Before starting Garage v0.8**, run the offline migration step: `garage offline-repair --yes object_counters`.
This can take a while to run, depending on the number of objects stored in your cluster.
8. Turn on Garage v0.8
9. Do `garage repair --all-nodes --yes tables` and `garage repair --all-nodes --yes blocks`.
Wait for a full table sync to run.
10. Your upgraded cluster should be in a working state. Re-enable API and Web
access and check that everything went well.
11. Monitor your cluster in the next hours to see if it works well under your production load, report any issue.
+28 -28
View File
@@ -206,8 +206,8 @@ and responses need to be translated.
Query parameters:
| name | default value | meaning |
| - | - | - |
| name | default value | meaning |
|------------|---------------|----------------------------------|
| `sort_key` | **mandatory** | The sort key of the item to read |
Returns the item with specified partition key and sort key. Values can be
@@ -317,11 +317,11 @@ an HTTP 304 NOT MODIFIED is returned.
Query parameters:
| name | default value | meaning |
| - | - | - |
| `sort_key` | **mandatory** | The sort key of the item to read |
| `causality_token` | **mandatory** | The causality token of the last known value or set of values |
| `timeout` | 300 | The timeout before 304 NOT MODIFIED is returned if the value isn't updated |
| name | default value | meaning |
|-------------------|---------------|----------------------------------------------------------------------------|
| `sort_key` | **mandatory** | The sort key of the item to read |
| `causality_token` | **mandatory** | The causality token of the last known value or set of values |
| `timeout` | 300 | The timeout before 304 NOT MODIFIED is returned if the value isn't updated |
The timeout can be set to any number of seconds, with a maximum of 600 seconds (10 minutes).
@@ -346,7 +346,7 @@ myblobblahblahblah
Example response:
```
HTTP/1.1 200 OK
HTTP/1.1 204 No Content
```
**DeleteItem: `DELETE /<bucket>/<partition key>?sort_key=<sort_key>`**
@@ -382,13 +382,13 @@ as these values are asynchronously updated, and thus eventually consistent.
Query parameters:
| name | default value | meaning |
| - | - | - |
| `prefix` | `null` | Restrict listing to partition keys that start with this prefix |
| `start` | `null` | First partition key to list, in lexicographical order |
| `end` | `null` | Last partition key to list (excluded) |
| `limit` | `null` | Maximum number of partition keys to list |
| `reverse` | `false` | Iterate in reverse lexicographical order |
| name | default value | meaning |
|-----------|---------------|----------------------------------------------------------------|
| `prefix` | `null` | Restrict listing to partition keys that start with this prefix |
| `start` | `null` | First partition key to list, in lexicographical order |
| `end` | `null` | Last partition key to list (excluded) |
| `limit` | `null` | Maximum number of partition keys to list |
| `reverse` | `false` | Iterate in reverse lexicographical order |
The response consists in a JSON object that repeats the parameters of the query and gives the result (see below).
@@ -512,7 +512,7 @@ POST /my_bucket HTTP/1.1
Example response:
```
HTTP/1.1 200 OK
HTTP/1.1 204 NO CONTENT
```
@@ -525,17 +525,17 @@ The request body is a JSON list of searches, that each specify a range of
items to get (to get single items, set `singleItem` to `true`). A search is a
JSON struct with the following fields:
| name | default value | meaning |
| - | - | - |
| `partitionKey` | **mandatory** | The partition key in which to search |
| `prefix` | `null` | Restrict items to list to those whose sort keys start with this prefix |
| `start` | `null` | The sort key of the first item to read |
| `end` | `null` | The sort key of the last item to read (excluded) |
| `limit` | `null` | The maximum number of items to return |
| `reverse` | `false` | Iterate in reverse lexicographical order on sort keys |
| `singleItem` | `false` | Whether to return only the item with sort key `start` |
| `conflictsOnly` | `false` | Whether to return only items that have several concurrent values |
| `tombstones` | `false` | Whether or not to return tombstone lines to indicate the presence of old deleted items |
| name | default value | meaning |
|-----------------|---------------|----------------------------------------------------------------------------------------|
| `partitionKey` | **mandatory** | The partition key in which to search |
| `prefix` | `null` | Restrict items to list to those whose sort keys start with this prefix |
| `start` | `null` | The sort key of the first item to read |
| `end` | `null` | The sort key of the last item to read (excluded) |
| `limit` | `null` | The maximum number of items to return |
| `reverse` | `false` | Iterate in reverse lexicographical order on sort keys |
| `singleItem` | `false` | Whether to return only the item with sort key `start` |
| `conflictsOnly` | `false` | Whether to return only items that have several concurrent values |
| `tombstones` | `false` | Whether or not to return tombstone lines to indicate the presence of old deleted items |
For each of the searches, triplets are listed and returned separately. The
@@ -683,7 +683,7 @@ POST /my_bucket?delete HTTP/1.1
Example response:
```
```json
HTTP/1.1 200 OK
[
+14
View File
@@ -0,0 +1,14 @@
*
!assets
!.gitignore
!*.svg
!*.png
!*.jpg
!*.tex
!Makefile
!.gitignore
!assets/*.drawio.pdf
!talk.pdf
+5
View File
@@ -0,0 +1,5 @@
talk.pdf: talk.tex assets/consistent_hashing_1.pdf assets/consistent_hashing_2.pdf assets/consistent_hashing_3.pdf assets/consistent_hashing_4.pdf assets/garage_tables.pdf assets/deuxfleurs.pdf
pdflatex talk.tex
assets/%.pdf: assets/%.svg
inkscape -D -z --file=$^ --export-pdf=$@
Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 184 KiB

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9198d0cfc0e04a56f84a353dd660b294ecf0dc68fe429892c8e9e604de016748
size 31966
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:14dbf4a45545889babb2fdc6580399811366f6cc786cb28e3467cbcedbfe9482
size 31688
Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:21a3bb004ad35c8b47f14195c720048f8db31f47bd6346c1d747000570149c67
size 31073
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c4e109078a784c3999ca2ccd1adda8ff5793d1b230a9e27b4ad2e419afa0d37d
size 25145
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 53 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 54 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 56 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 57 KiB

@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 70.424515 70.300102"
version="1.1"
id="svg8"
sodipodi:docname="logo.svg"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
inkscape:export-filename="/home/quentin/Documents/dev/deuxfleurs/site/src/img/logo.png"
inkscape:export-xdpi="699.30194"
inkscape:export-ydpi="699.30194"
width="70.424515"
height="70.300102"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs12" />
<sodipodi:namedview
id="namedview10"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="12.125"
inkscape:cx="43.092783"
inkscape:cy="48.082474"
inkscape:window-width="3072"
inkscape:window-height="1659"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg8" />
<g
id="g79969"
transform="translate(-0.827,34.992103)">
<path
fill="#ffffff"
d="m 15.632,34.661 c -0.799,-0.597 -1.498,-1.484 -2.035,-2.592 l -0.228,-0.47 -0.46,0.249 c -0.975,0.528 -1.913,0.858 -2.744,0.969 L 9.963,29.061 6.327,30.029 C 6.17,29.175 6.202,28.142 6.423,27.007 L 6.526,26.482 5.994,26.416 C 4.752,26.262 3.688,25.891 2.89,25.336 L 4.411,22.419 1.423,20.896 C 1.742,19.952 2.371,19.014 3.257,18.161 L 3.634,17.798 3.255,17.438 C 2.452,16.674 1.847,15.884 1.485,15.127 L 4.995,13.774 2.95,10.615 C 3.69,10.213 4.643,9.929 5.739,9.783 L 6.258,9.715 6.167,9.201 C 5.952,7.99 5.995,6.863 6.291,5.913 l 3.308,0.523 0.524,-3.308 c 0.988,0.013 2.08,0.326 3.164,0.907 L 13.749,4.283 13.975,3.81 C 14.454,2.807 15.019,1.986 15.628,1.406 L 18,4.326 20.372,1.406 c 0.609,0.58 1.175,1.401 1.653,2.404 l 0.226,0.473 0.462,-0.247 C 23.798,3.455 24.891,3.142 25.877,3.13 L 26.4,6.438 29.71,5.913 c 0.296,0.951 0.34,2.078 0.124,3.288 l -0.092,0.515 0.518,0.069 c 1.095,0.145 2.048,0.43 2.788,0.832 l -2.046,3.156 3.511,1.355 c -0.361,0.757 -0.966,1.547 -1.77,2.311 l -0.379,0.36 0.377,0.363 c 0.888,0.854 1.516,1.793 1.835,2.736 l -2.984,1.52 1.521,2.984 c -0.812,0.574 -1.871,0.964 -3.094,1.134 l -0.518,0.072 0.096,0.514 c 0.201,1.089 0.226,2.083 0.073,2.909 l -3.634,-0.97 -0.204,3.757 c -0.83,-0.11 -1.768,-0.44 -2.742,-0.968 l -0.459,-0.249 -0.228,0.47 c -0.539,1.107 -1.237,1.994 -2.036,2.591 L 18,32.293 Z"
id="path2" />
<path
d="M 7.092,10.678 C 6.562,9.189 6.394,7.708 6.66,6.478 l 2.368,0.375 0.987,0.156 0.157,-0.988 0.375,-2.368 C 11.808,3.78 13.16,4.396 14.409,5.359 14.527,5.022 14.653,4.696 14.791,4.392 13.24,3.257 11.568,2.629 10.061,2.629 9.938,2.629 9.816,2.633 9.695,2.642 L 9.184,5.865 5.96,5.354 C 5.36,6.841 5.395,8.769 6.045,10.747 6.38,10.71 6.729,10.686 7.092,10.678 Z M 21.593,5.359 c 1.248,-0.962 2.6,-1.578 3.86,-1.705 l 0.376,2.368 0.156,0.988 0.987,-0.157 2.369,-0.376 c 0.266,1.23 0.098,2.71 -0.432,4.2 0.361,0.009 0.711,0.032 1.046,0.07 C 30.606,8.769 30.64,6.841 30.04,5.353 L 26.815,5.865 26.304,2.641 c -0.12,-0.008 -0.242,-0.012 -0.365,-0.012 -1.507,0 -3.179,0.628 -4.73,1.762 0.14,0.306 0.266,0.631 0.384,0.968 z M 7.368,27 h 0.035 c 0.067,0 0.157,-0.604 0.26,-0.947 -0.098,0.004 -0.197,0.046 -0.294,0.046 -1.496,0 -2.826,-0.303 -3.83,-0.89 L 4.628,23.081 5.082,22.194 4.191,21.742 2.055,20.654 C 2.563,19.503 3.57,18.404 4.873,17.511 4.586,17.292 4.312,17.07 4.063,16.842 2.376,18.059 1.217,19.597 0.828,21.152 l 2.908,1.483 -1.482,2.843 C 3.475,26.501 5.303,27 7.368,27 Z m 27.806,-5.846 c -0.39,-1.555 -1.548,-3.093 -3.234,-4.311 -0.25,0.228 -0.523,0.451 -0.81,0.669 1.304,0.893 2.31,1.992 2.817,3.145 l -2.136,1.088 -0.891,0.453 0.454,0.892 1.089,2.137 c -1.004,0.587 -2.332,0.904 -3.828,0.904 -0.099,0 -0.199,-0.01 -0.299,-0.013 0.103,0.344 0.192,0.683 0.26,1.011 l 0.039,0.002 c 2.066,0 3.892,-0.563 5.112,-1.587 l -1.482,-2.908 z m -12.653,9.182 c -0.447,1.517 -1.181,2.812 -2.119,3.651 L 18.707,32.293 18,31.586 l -0.707,0.707 -1.695,1.694 c -0.938,-0.839 -1.673,-2.136 -2.12,-3.652 -0.296,0.206 -0.593,0.397 -0.886,0.563 0.636,1.98 1.741,3.559 3.1,4.409 L 18,33 l 2.308,2.308 c 1.358,-0.851 2.464,-2.428 3.101,-4.408 -0.295,-0.168 -0.591,-0.359 -0.888,-0.564 z"
fill="#ea596e"
id="path4" />
<path
fill="#ea596e"
d="m 20.118,5.683 c 0.426,1.146 0.748,2.596 0.841,4.284 l 0.2,3.683 3.564,-0.946 c 1.32,-0.351 2.655,-0.536 3.86,-0.536 0.16,0 0.318,0.003 0.474,0.01 l -1.827,2.819 3.139,1.211 c -0.958,0.759 -2.237,1.514 -3.814,2.123 l -3.441,1.328 2.001,3.099 c 0.918,1.42 1.509,2.782 1.838,3.96 L 23.709,25.853 23.527,29.21 C 22.508,28.533 21.395,27.55 20.329,26.237 L 18,23.374 15.672,26.236 c -1.066,1.312 -2.179,2.295 -3.198,2.972 l -0.18,-3.354 -3.248,0.864 c 0.329,-1.178 0.921,-2.54 1.839,-3.961 L 12.889,19.658 9.447,18.33 C 7.87,17.721 6.591,16.967 5.633,16.208 L 8.768,15 6.941,12.177 c 0.155,-0.006 0.313,-0.01 0.473,-0.01 1.206,0 2.541,0.185 3.861,0.536 l 3.564,0.947 0.202,-3.683 c 0.092,-1.688 0.415,-3.138 0.84,-4.284 L 18,8.292 20.118,5.683 M 20.308,0.692 18,3.533 15.692,0.692 C 13.703,2.224 12.271,5.684 12.046,9.804 10.429,9.374 8.854,9.167 7.414,9.167 c -2.11,0 -3.929,0.445 -5.161,1.289 l 1.989,3.073 -3.415,1.316 c 0.842,2.366 3.69,4.797 7.54,6.283 -2.241,3.465 -3.116,7.106 -2.407,9.516 l 3.537,-0.941 0.196,3.654 c 2.512,-0.07 5.703,-2.027 8.307,-5.228 2.603,3.201 5.796,5.158 8.306,5.228 l 0.198,-3.655 3.535,0.943 c 0.71,-2.411 -0.165,-6.05 -2.404,-9.517 3.849,-1.485 6.696,-3.918 7.538,-6.283 l -3.415,-1.318 1.99,-3.07 c -1.233,-0.844 -3.053,-1.29 -5.164,-1.29 -1.438,0 -3.013,0.207 -4.63,0.636 C 23.729,5.684 22.297,2.224 20.308,0.692 Z"
id="path6" />
</g>
<g
id="g79964"
transform="translate(-1.043816,35.993714)">
<path
fill="#ffffff"
d="m 51.92633,-2.0247139 c -0.799,-0.597 -1.498,-1.484 -2.035,-2.592 l -0.228,-0.47 -0.46,0.249 c -0.975,0.528 -1.913,0.858 -2.744,0.969 l -0.202,-3.7560001 -3.636,0.968 c -0.157,-0.854 -0.125,-1.887 0.096,-3.022 l 0.103,-0.525 -0.532,-0.066 c -1.242,-0.154 -2.306,-0.525 -3.104,-1.08 l 1.521,-2.917 -2.988,-1.523 c 0.319,-0.944 0.948,-1.882 1.834,-2.735 l 0.377,-0.363 -0.379,-0.36 c -0.803,-0.764 -1.408,-1.554 -1.77,-2.311 l 3.51,-1.353 -2.045,-3.159 c 0.74,-0.402 1.693,-0.686 2.789,-0.832 l 0.519,-0.068 -0.091,-0.514 c -0.215,-1.211 -0.172,-2.338 0.124,-3.288 l 3.308,0.523 0.524,-3.308 c 0.988,0.013 2.08,0.326 3.164,0.907 l 0.462,0.248 0.226,-0.473 c 0.479,-1.003 1.044,-1.824 1.653,-2.404 l 2.372,2.92 2.372,-2.92 c 0.609,0.58 1.175,1.401 1.653,2.404 l 0.226,0.473 0.462,-0.247 c 1.085,-0.581 2.178,-0.894 3.164,-0.906 l 0.523,3.308 3.31,-0.525 c 0.296,0.951 0.34,2.078 0.124,3.288 l -0.092,0.515 0.518,0.069 c 1.095,0.145 2.048,0.43 2.788,0.832 l -2.046,3.156 3.511,1.355 c -0.361,0.757 -0.966,1.547 -1.77,2.311 l -0.379,0.36 0.377,0.363 c 0.888,0.854 1.516,1.793 1.835,2.736 l -2.984,1.52 1.521,2.984 c -0.812,0.574 -1.871,0.964 -3.094,1.134 l -0.518,0.072 0.096,0.514 c 0.201,1.089 0.226,2.083 0.073,2.909 l -3.634,-0.97 -0.204,3.7570001 c -0.83,-0.11 -1.768,-0.44 -2.742,-0.968 l -0.459,-0.249 -0.228,0.47 c -0.539,1.107 -1.237,1.994 -2.036,2.591 l -2.367,-2.369 z"
id="path2-9" />
<path
d="m 43.38633,-26.007714 c -0.53,-1.489 -0.698,-2.97 -0.432,-4.2 l 2.368,0.375 0.987,0.156 0.157,-0.988 0.375,-2.368 c 1.261,0.127 2.613,0.743 3.862,1.706 0.118,-0.337 0.244,-0.663 0.382,-0.967 -1.551,-1.135 -3.223,-1.763 -4.73,-1.763 -0.123,0 -0.245,0.004 -0.366,0.013 l -0.511,3.223 -3.224,-0.511 c -0.6,1.487 -0.565,3.415 0.085,5.393 0.335,-0.037 0.684,-0.061 1.047,-0.069 z m 14.501,-5.319 c 1.248,-0.962 2.6,-1.578 3.86,-1.705 l 0.376,2.368 0.156,0.988 0.987,-0.157 2.369,-0.376 c 0.266,1.23 0.098,2.71 -0.432,4.2 0.361,0.009 0.711,0.032 1.046,0.07 0.651,-1.978 0.685,-3.906 0.085,-5.394 l -3.225,0.512 -0.511,-3.224 c -0.12,-0.008 -0.242,-0.012 -0.365,-0.012 -1.507,0 -3.179,0.628 -4.73,1.762 0.14,0.306 0.266,0.631 0.384,0.968 z m -14.225,21.641 h 0.035 c 0.067,0 0.157,-0.604 0.26,-0.947 -0.098,0.004 -0.197,0.046 -0.294,0.046 -1.496,0 -2.826,-0.303 -3.83,-0.89 l 1.089,-2.128 0.454,-0.887 -0.891,-0.452 -2.136,-1.088 c 0.508,-1.151 1.515,-2.25 2.818,-3.143 -0.287,-0.219 -0.561,-0.441 -0.81,-0.669 -1.687,1.217 -2.846,2.755 -3.235,4.31 l 2.908,1.483 -1.482,2.843 c 1.221,1.023 3.049,1.522 5.114,1.522 z m 27.806,-5.846 c -0.39,-1.555 -1.548,-3.093 -3.234,-4.311 -0.25,0.228 -0.523,0.451 -0.81,0.669 1.304,0.893 2.31,1.992 2.817,3.145 l -2.136,1.088 -0.891,0.453 0.454,0.892 1.089,2.137 c -1.004,0.587 -2.332,0.904 -3.828,0.904 -0.099,0 -0.199,-0.01 -0.299,-0.013 0.103,0.344 0.192,0.683 0.26,1.011 l 0.039,0.002 c 2.066,0 3.892,-0.563 5.112,-1.587 l -1.482,-2.908 z m -12.653,9.182 c -0.447,1.5170001 -1.181,2.8120001 -2.119,3.6510001 l -1.695,-1.694 -0.707,-0.707 -0.707,0.707 -1.695,1.694 c -0.938,-0.839 -1.673,-2.136 -2.12,-3.6520001 -0.296,0.2060001 -0.593,0.3970001 -0.886,0.5630001 0.636,1.98 1.741,3.559 3.1,4.409 l 2.308,-2.307 2.308,2.308 c 1.358,-0.851 2.464,-2.428 3.101,-4.408 -0.295,-0.168 -0.591,-0.359 -0.888,-0.5640001 z"
fill="#ea596e"
id="path4-3" />
<path
fill="#ea596e"
d="m 56.41233,-31.002714 c 0.426,1.146 0.748,2.596 0.841,4.284 l 0.2,3.683 3.564,-0.946 c 1.32,-0.351 2.655,-0.536 3.86,-0.536 0.16,0 0.318,0.003 0.474,0.01 l -1.827,2.819 3.139,1.211 c -0.958,0.759 -2.237,1.514 -3.814,2.123 l -3.441,1.328 2.001,3.099 c 0.918,1.42 1.509,2.782 1.838,3.96 l -3.244,-0.865 -0.182,3.357 c -1.019,-0.677 -2.132,-1.66 -3.198,-2.973 l -2.329,-2.863 -2.328,2.862 c -1.066,1.312 -2.179,2.295 -3.198,2.972 l -0.18,-3.354 -3.248,0.864 c 0.329,-1.178 0.921,-2.54 1.839,-3.961 l 2.004,-3.099 -3.442,-1.328 c -1.577,-0.609 -2.856,-1.363 -3.814,-2.122 l 3.135,-1.208 -1.827,-2.823 c 0.155,-0.006 0.313,-0.01 0.473,-0.01 1.206,0 2.541,0.185 3.861,0.536 l 3.564,0.947 0.202,-3.683 c 0.092,-1.688 0.415,-3.138 0.84,-4.284 l 2.119,2.609 2.118,-2.609 m 0.19,-4.991 -2.308,2.841 -2.308,-2.841 c -1.989,1.532 -3.421,4.992 -3.646,9.112 -1.617,-0.43 -3.192,-0.637 -4.632,-0.637 -2.11,0 -3.929,0.445 -5.161,1.289 l 1.989,3.073 -3.415,1.316 c 0.842,2.366 3.69,4.797 7.54,6.283 -2.241,3.465 -3.116,7.106 -2.407,9.5160001 l 3.537,-0.9410001 0.196,3.6540001 c 2.512,-0.07 5.703,-2.027 8.307,-5.2280001 2.603,3.2010001 5.796,5.1580001 8.306,5.2280001 l 0.198,-3.6550001 3.535,0.9430001 c 0.71,-2.4110001 -0.165,-6.0500001 -2.404,-9.5170001 3.849,-1.485 6.696,-3.918 7.538,-6.283 l -3.415,-1.318 1.99,-3.07 c -1.233,-0.844 -3.053,-1.29 -5.164,-1.29 -1.438,0 -3.013,0.207 -4.63,0.636 -0.225,-4.119 -1.657,-7.579 -3.646,-9.111 z"
id="path6-6" />
</g>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:42.6667px;line-height:1.25;font-family:sans-serif;fill:#ea596e;fill-opacity:1;stroke:none"
x="2.2188232"
y="31.430677"
id="text46212"><tspan
sodipodi:role="line"
id="tspan46210"
x="2.2188232"
y="31.430677"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:42.6667px;font-family:'TeX Gyre Termes';-inkscape-font-specification:'TeX Gyre Termes'">D</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:42.6667px;line-height:1.25;font-family:sans-serif;fill:#ea596e;fill-opacity:1;stroke:none"
x="41.347008"
y="67.114784"
id="text46212-1"><tspan
sodipodi:role="line"
id="tspan46210-5"
x="41.347008"
y="67.114784"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:42.6667px;font-family:'TeX Gyre Termes';-inkscape-font-specification:'TeX Gyre Termes'">F</tspan></text>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0d9ca1a04f943664e14d0e1edbde596789e4798f8ad5e1cff68bc9dd0cc1334f
size 26098
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bffa5d4a72ad25e0b18b43022258a2e4471ce94ff5397aab892fe43ec9d4d7d5
size 33911
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9c4913b52e84b5af3f8effbcb7e2060845a8c6f43d5b38072b9ee33c0300d49f
size 31051
@@ -0,0 +1,537 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="850"
height="480"
viewBox="0 0 224.89584 127"
version="1.1"
id="svg8"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
sodipodi:docname="garage_tables.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs2">
<marker
style="overflow:visible"
id="marker1262"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Mend"
inkscape:isstock="true">
<path
transform="matrix(-0.4,0,0,-0.4,-4,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path1260" />
</marker>
<marker
style="overflow:visible"
id="Arrow1Mend"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Mend"
inkscape:isstock="true"
inkscape:collect="always">
<path
transform="matrix(-0.4,0,0,-0.4,-4,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path965" />
</marker>
<marker
style="overflow:visible"
id="Arrow1Lend"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Lend"
inkscape:isstock="true">
<path
transform="matrix(-0.8,0,0,-0.8,-10,0)"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path959" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.98994949"
inkscape:cx="429.31483"
inkscape:cy="289.40871"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
units="px"
inkscape:window-width="1678"
inkscape:window-height="993"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:showpageshadow="2"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="39.570904"
y="38.452755"
id="text2025"><tspan
sodipodi:role="line"
id="tspan2023"
x="39.570904"
y="38.452755"
style="font-size:5.64444px;stroke-width:0.264583" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="101.95796"
y="92.835831"
id="text2139"><tspan
sodipodi:role="line"
id="tspan2137"
x="101.95796"
y="92.835831"
style="stroke-width:0.264583"> </tspan></text>
<g
id="g2316"
transform="translate(-11.455511,1.5722486)">
<g
id="g2277">
<rect
style="fill:none;stroke:#000000;stroke-width:0.8;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect833"
width="47.419891"
height="95.353409"
x="18.534418"
y="24.42766" />
<rect
style="fill:none;stroke:#000000;stroke-width:0.799999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect833-3"
width="47.419891"
height="86.973076"
x="18.534418"
y="32.807987" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="32.250839"
y="29.894743"
id="text852"><tspan
sodipodi:role="line"
id="tspan850"
x="32.250839"
y="29.894743"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">Object</tspan></text>
</g>
<g
id="g2066"
transform="translate(-2.1807817,-3.0621439)">
<g
id="g1969"
transform="matrix(0.12763631,0,0,0.12763631,0.7215051,24.717273)"
style="fill:#ff6600;fill-opacity:1;stroke:none;stroke-opacity:1">
<path
style="fill:#ff6600;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-opacity:1"
d="m 203.71837,154.80038 c -1.11451,3.75057 -2.45288,5.84095 -5.11132,7.98327 -2.2735,1.83211 -4.66721,2.65982 -8.09339,2.79857 -2.59227,0.10498 -2.92868,0.0577 -5.02863,-0.70611 -3.99215,-1.45212 -7.1627,-4.65496 -8.48408,-8.57046 -1.28374,-3.80398 -0.61478,-8.68216 1.64793,-12.01698 0.87317,-1.28689 3.15089,-3.48326 4.18771,-4.03815 l 0.53332,-28.51234 5.78454,-5.09197 6.95158,6.16704 -3.21112,3.49026 3.17616,3.45499 -3.17616,3.40822 2.98973,3.28645 -3.24843,3.3829 4.49203,4.58395 0.0516,5.69106 c 1.06874,0.64848 3.81974,3.24046 4.69548,4.56257 0.452,0.68241 1.06834,2.0197 1.36962,2.97176 0.62932,1.98864 0.88051,5.785 0.47342,7.15497 z m -10.0406,2.32604 c -0.88184,-3.17515 -4.92402,-3.78864 -6.75297,-1.02492 -0.58328,0.8814 -0.6898,1.28852 -0.58362,2.23056 0.26492,2.35041 2.45434,3.95262 4.60856,3.37255 1.19644,-0.32217 2.39435,-1.44872 2.72875,-2.56621 0.30682,-1.02529 0.30686,-0.9045 -7.9e-4,-2.01198 z"
id="path1971"
sodipodi:nodetypes="ssscsscccccccccccssscsssscc" />
</g>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="28.809687"
y="44.070885"
id="text852-9"><tspan
sodipodi:role="line"
id="tspan850-4"
x="28.809687"
y="44.070885"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">bucket </tspan></text>
</g>
<g
id="g2066-7"
transform="translate(-2.1807817,6.2627616)">
<g
id="g1969-8"
transform="matrix(0.12763631,0,0,0.12763631,0.7215051,24.717273)"
style="fill:#ff6600;fill-opacity:1;stroke:none;stroke-opacity:1">
<path
style="fill:#4040ff;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-opacity:1"
d="m 203.71837,154.80038 c -1.11451,3.75057 -2.45288,5.84095 -5.11132,7.98327 -2.2735,1.83211 -4.66721,2.65982 -8.09339,2.79857 -2.59227,0.10498 -2.92868,0.0577 -5.02863,-0.70611 -3.99215,-1.45212 -7.1627,-4.65496 -8.48408,-8.57046 -1.28374,-3.80398 -0.61478,-8.68216 1.64793,-12.01698 0.87317,-1.28689 3.15089,-3.48326 4.18771,-4.03815 l 0.53332,-28.51234 5.78454,-5.09197 6.95158,6.16704 -3.21112,3.49026 3.17616,3.45499 -3.17616,3.40822 2.98973,3.28645 -3.24843,3.3829 4.49203,4.58395 0.0516,5.69106 c 1.06874,0.64848 3.81974,3.24046 4.69548,4.56257 0.452,0.68241 1.06834,2.0197 1.36962,2.97176 0.62932,1.98864 0.88051,5.785 0.47342,7.15497 z m -10.0406,2.32604 c -0.88184,-3.17515 -4.92402,-3.78864 -6.75297,-1.02492 -0.58328,0.8814 -0.6898,1.28852 -0.58362,2.23056 0.26492,2.35041 2.45434,3.95262 4.60856,3.37255 1.19644,-0.32217 2.39435,-1.44872 2.72875,-2.56621 0.30682,-1.02529 0.30686,-0.9045 -7.9e-4,-2.01198 z"
id="path1971-4"
sodipodi:nodetypes="ssscsscccccccccccssscsssscc" />
</g>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="28.809687"
y="44.070885"
id="text852-9-5"><tspan
sodipodi:role="line"
id="tspan850-4-0"
x="28.809687"
y="44.070885"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">file path </tspan></text>
<path
style="fill:#ff6600;fill-opacity:1;stroke:none;stroke-width:0.0337704;stroke-opacity:1"
d="m 174.20027,104.45585 c -0.14225,0.47871 -0.31308,0.74552 -0.65239,1.01896 -0.29018,0.23384 -0.5957,0.33949 -1.03301,0.3572 -0.33087,0.0134 -0.37381,0.007 -0.64184,-0.0901 -0.50954,-0.18534 -0.91422,-0.59414 -1.08287,-1.0939 -0.16385,-0.48552 -0.0785,-1.10816 0.21033,-1.5338 0.11145,-0.16426 0.40217,-0.44459 0.53451,-0.51542 l 0.0681,-3.639207 0.73832,-0.64992 0.88727,0.787138 -0.40986,0.445484 0.4054,0.440982 -0.4054,0.435013 0.3816,0.41947 -0.41461,0.43178 0.57334,0.58508 0.007,0.72639 c 0.13641,0.0828 0.48753,0.4136 0.59931,0.58235 0.0577,0.0871 0.13636,0.25778 0.17481,0.3793 0.0803,0.25382 0.11239,0.73838 0.0604,0.91323 z m -1.28154,0.29689 c -0.11256,-0.40526 -0.62849,-0.48357 -0.86193,-0.13082 -0.0745,0.1125 -0.088,0.16447 -0.0745,0.2847 0.0338,0.3 0.31326,0.5045 0.58822,0.43046 0.15271,-0.0411 0.30561,-0.1849 0.34829,-0.32754 0.0392,-0.13086 0.0392,-0.11544 -1e-4,-0.2568 z"
id="path1971-3"
sodipodi:nodetypes="ssscsscccccccccccssscsssscc" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="177.8474"
y="104.05132"
id="text852-9-6"><tspan
sodipodi:role="line"
id="tspan850-4-7"
x="177.8474"
y="104.05132"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">= partition key </tspan></text>
<path
style="fill:#4040ff;fill-opacity:1;stroke:none;stroke-width:0.0337704;stroke-opacity:1"
d="m 174.20027,113.78076 c -0.14225,0.47871 -0.31308,0.74552 -0.65239,1.01895 -0.29018,0.23385 -0.5957,0.33949 -1.03301,0.3572 -0.33087,0.0134 -0.37381,0.007 -0.64184,-0.0901 -0.50954,-0.18534 -0.91422,-0.59414 -1.08287,-1.0939 -0.16385,-0.48553 -0.0785,-1.10816 0.21033,-1.53381 0.11145,-0.16425 0.40217,-0.44459 0.53451,-0.51541 l 0.0681,-3.63921 0.73832,-0.64992 0.88727,0.78714 -0.40986,0.44548 0.4054,0.44098 -0.4054,0.43502 0.3816,0.41947 -0.41461,0.43178 0.57334,0.58508 0.007,0.72638 c 0.13641,0.0828 0.48753,0.4136 0.59931,0.58235 0.0577,0.0871 0.13636,0.25779 0.17481,0.37931 0.0803,0.25382 0.11239,0.73837 0.0604,0.91323 z m -1.28154,0.29689 c -0.11256,-0.40527 -0.62849,-0.48357 -0.86193,-0.13082 -0.0745,0.1125 -0.088,0.16446 -0.0745,0.2847 0.0338,0.3 0.31326,0.5045 0.58822,0.43046 0.15271,-0.0411 0.30561,-0.18491 0.34829,-0.32754 0.0392,-0.13087 0.0392,-0.11545 -1e-4,-0.2568 z"
id="path1971-4-5"
sodipodi:nodetypes="ssscsscccccccccccssscsssscc" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="177.8474"
y="113.37622"
id="text852-9-5-3"><tspan
sodipodi:role="line"
id="tspan850-4-0-5"
x="177.8474"
y="113.37622"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">= sort key </tspan></text>
</g>
<g
id="g2161"
transform="translate(-62.264403,-59.333115)">
<g
id="g2271"
transform="translate(0,67.042823)">
<rect
style="fill:none;stroke:#000000;stroke-width:0.799999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect833-6"
width="39.008453"
height="16.775949"
x="84.896881"
y="90.266838" />
<rect
style="fill:none;stroke:#000000;stroke-width:0.799999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect833-3-1"
width="39.008453"
height="8.673645"
x="84.896881"
y="98.369141" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="89.826942"
y="96.212921"
id="text852-0"><tspan
sodipodi:role="line"
id="tspan850-6"
x="89.826942"
y="96.212921"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">Version 1</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="89.826942"
y="104.71013"
id="text852-0-3"><tspan
sodipodi:role="line"
id="tspan850-6-2"
x="89.826942"
y="104.71013"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';fill:#4d4d4d;stroke-width:0.264583">deleted</tspan></text>
</g>
</g>
<g
id="g2263"
transform="translate(0,-22.791204)">
<g
id="g2161-1"
transform="translate(-62.264403,-10.910843)">
<rect
style="fill:none;stroke:#000000;stroke-width:0.799999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect833-6-5"
width="39.008453"
height="36.749603"
x="84.896881"
y="90.266838" />
<rect
style="fill:none;stroke:#000000;stroke-width:0.799999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect833-3-1-5"
width="39.008453"
height="28.647301"
x="84.896881"
y="98.369141" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="89.826942"
y="96.212921"
id="text852-0-4"><tspan
sodipodi:role="line"
id="tspan850-6-7"
x="89.826942"
y="96.212921"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">Version 2</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="89.826942"
y="104.71013"
id="text852-0-3-6"><tspan
sodipodi:role="line"
id="tspan850-6-2-5"
x="89.826942"
y="104.71013"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';fill:#000000;stroke-width:0.264583">id</tspan></text>
</g>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="27.56254"
y="100.34132"
id="text852-0-3-6-6"><tspan
sodipodi:role="line"
id="tspan850-6-2-5-9"
x="27.56254"
y="100.34132"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';fill:#000000;stroke-width:0.264583">size</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="27.56254"
y="106.90263"
id="text852-0-3-6-6-3"><tspan
sodipodi:role="line"
id="tspan850-6-2-5-9-7"
x="27.56254"
y="106.90263"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';fill:#000000;stroke-width:0.264583">MIME type</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="27.56254"
y="111.92816"
id="text852-0-3-6-6-3-4"><tspan
sodipodi:role="line"
id="tspan850-6-2-5-9-7-5"
x="27.56254"
y="111.92816"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';fill:#000000;stroke-width:0.264583">...</tspan></text>
</g>
</g>
<g
id="g898"
transform="translate(-6.2484318,29.95006)">
<rect
style="fill:none;stroke:#000000;stroke-width:0.799999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect833-7"
width="47.419891"
height="44.007515"
x="95.443573"
y="24.42766" />
<rect
style="fill:none;stroke:#000000;stroke-width:0.799999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect833-3-4"
width="47.419891"
height="35.627186"
x="95.443573"
y="32.807987" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="107.46638"
y="29.894743"
id="text852-4"><tspan
sodipodi:role="line"
id="tspan850-3"
x="107.46638"
y="29.894743"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">Version</tspan></text>
<path
style="fill:#ff6600;fill-opacity:1;stroke:none;stroke-width:0.0337704;stroke-opacity:1"
d="m 102.90563,41.413279 c -0.14226,0.478709 -0.31308,0.745518 -0.65239,1.018956 -0.29019,0.233843 -0.59571,0.339489 -1.03301,0.357199 -0.33087,0.0134 -0.37381,0.0074 -0.64184,-0.09013 -0.50954,-0.185343 -0.914221,-0.594142 -1.082877,-1.093901 -0.163852,-0.485526 -0.07847,-1.108159 0.210335,-1.533803 0.111448,-0.164254 0.402172,-0.444591 0.534502,-0.515415 l 0.0681,-3.63921 0.73832,-0.64992 0.88727,0.787138 -0.40985,0.445484 0.40539,0.440982 -0.40539,0.435013 0.3816,0.41947 -0.41462,0.431781 0.57335,0.585078 0.007,0.726386 c 0.13641,0.08277 0.48753,0.413601 0.59931,0.58235 0.0577,0.0871 0.13636,0.257787 0.17481,0.379304 0.0803,0.253823 0.11239,0.738377 0.0604,0.913234 z m -1.28155,0.296888 c -0.11255,-0.405265 -0.62848,-0.483569 -0.86192,-0.130817 -0.0744,0.112498 -0.088,0.164461 -0.0745,0.2847 0.0338,0.299998 0.31326,0.504498 0.58822,0.43046 0.15271,-0.04112 0.3056,-0.184909 0.34828,-0.327542 0.0392,-0.130864 0.0392,-0.115447 -1e-4,-0.256801 z"
id="path1971-0"
sodipodi:nodetypes="ssscsscccccccccccssscsssscc" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="104.99195"
y="41.008743"
id="text852-9-7"><tspan
sodipodi:role="line"
id="tspan850-4-8"
x="104.99195"
y="41.008743"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">id </tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="104.99195"
y="49.168018"
id="text852-9-7-6"><tspan
sodipodi:role="line"
id="tspan850-4-8-8"
x="104.99195"
y="49.168018"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">h(block 1)</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="104.99195"
y="56.583336"
id="text852-9-7-6-8"><tspan
sodipodi:role="line"
id="tspan850-4-8-8-4"
x="104.99195"
y="56.583336"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">h(block 2)</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="104.99195"
y="64.265732"
id="text852-9-7-6-3"><tspan
sodipodi:role="line"
id="tspan850-4-8-8-1"
x="104.99195"
y="64.265732"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">...</tspan></text>
</g>
<g
id="g898-3"
transform="translate(75.777779,38.888663)">
<rect
style="fill:none;stroke:#000000;stroke-width:0.799999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect833-7-6"
width="47.419891"
height="29.989157"
x="95.443573"
y="24.42766" />
<rect
style="fill:none;stroke:#000000;stroke-width:0.799999;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect833-3-4-7"
width="47.419891"
height="21.608831"
x="95.443573"
y="32.807987" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="102.11134"
y="29.894743"
id="text852-4-5"><tspan
sodipodi:role="line"
id="tspan850-3-3"
x="102.11134"
y="29.894743"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">Data block</tspan></text>
<path
style="fill:#ff6600;fill-opacity:1;stroke:none;stroke-width:0.0337704;stroke-opacity:1"
d="m 102.90563,41.413279 c -0.14226,0.478709 -0.31308,0.745518 -0.65239,1.018956 -0.29019,0.233843 -0.59571,0.339489 -1.03301,0.357199 -0.33087,0.0134 -0.37381,0.0074 -0.64184,-0.09013 -0.50954,-0.185343 -0.914221,-0.594142 -1.082877,-1.093901 -0.163852,-0.485526 -0.07847,-1.108159 0.210335,-1.533803 0.111448,-0.164254 0.402172,-0.444591 0.534502,-0.515415 l 0.0681,-3.63921 0.73832,-0.64992 0.88727,0.787138 -0.40985,0.445484 0.40539,0.440982 -0.40539,0.435013 0.3816,0.41947 -0.41462,0.431781 0.57335,0.585078 0.007,0.726386 c 0.13641,0.08277 0.48753,0.413601 0.59931,0.58235 0.0577,0.0871 0.13636,0.257787 0.17481,0.379304 0.0803,0.253823 0.11239,0.738377 0.0604,0.913234 z m -1.28155,0.296888 c -0.11255,-0.405265 -0.62848,-0.483569 -0.86192,-0.130817 -0.0744,0.112498 -0.088,0.164461 -0.0745,0.2847 0.0338,0.299998 0.31326,0.504498 0.58822,0.43046 0.15271,-0.04112 0.3056,-0.184909 0.34828,-0.327542 0.0392,-0.130864 0.0392,-0.115447 -1e-4,-0.256801 z"
id="path1971-0-5"
sodipodi:nodetypes="ssscsscccccccccccssscsssscc" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="104.99195"
y="41.008743"
id="text852-9-7-62"><tspan
sodipodi:role="line"
id="tspan850-4-8-9"
x="104.99195"
y="41.008743"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">hash </tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="104.99195"
y="49.168018"
id="text852-9-7-6-1"><tspan
sodipodi:role="line"
id="tspan850-4-8-8-2"
x="104.99195"
y="49.168018"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">data</tspan></text>
</g>
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Mend)"
d="M 42.105292,69.455903 89.563703,69.317144"
id="path954"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker1262)"
d="m 134.32612,77.363197 38.12618,0.260865"
id="path1258"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="8.6727352"
y="16.687063"
id="text852-3"><tspan
sodipodi:role="line"
id="tspan850-67"
x="8.6727352"
y="16.687063"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">Objects table </tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="89.190445"
y="16.687063"
id="text852-3-5"><tspan
sodipodi:role="line"
id="tspan850-67-3"
x="89.190445"
y="16.687063"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">Versions table </tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="174.55702"
y="16.687063"
id="text852-3-56"><tspan
sodipodi:role="line"
id="tspan850-67-2"
x="174.55702"
y="16.687063"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.64444px;font-family:'Liberation Mono';-inkscape-font-specification:'Liberation Mono Bold';stroke-width:0.264583">Blocks table</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 315 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 286 KiB

+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f245bb017eb85a227d96e576ae821f165ef478d46de02535c196638aa5fc84b0
size 2575953
+480
View File
@@ -0,0 +1,480 @@
%\nonstopmode
\documentclass[aspectratio=169]{beamer}
\usepackage[utf8]{inputenc}
% \usepackage[frenchb]{babel}
\usepackage{amsmath}
\usepackage{mathtools}
\usepackage{breqn}
\usepackage{multirow}
\usetheme{boxes}
\usepackage{graphicx}
\usepackage{adjustbox}
%\useoutertheme[footline=authortitle,subsection=false]{miniframes}
%\useoutertheme[footline=authorinstitute,subsection=false]{miniframes}
\useoutertheme{infolines}
\setbeamertemplate{headline}{}
\beamertemplatenavigationsymbolsempty
\definecolor{TitleOrange}{RGB}{255,137,0}
\setbeamercolor{title}{fg=TitleOrange}
\setbeamercolor{frametitle}{fg=TitleOrange}
\definecolor{ListOrange}{RGB}{255,145,5}
\setbeamertemplate{itemize item}{\color{ListOrange}$\blacktriangleright$}
\definecolor{verygrey}{RGB}{70,70,70}
\setbeamercolor{normal text}{fg=verygrey}
\usepackage{tabu}
\usepackage{multicol}
\usepackage{vwcol}
\usepackage{stmaryrd}
\usepackage{graphicx}
\usepackage[normalem]{ulem}
\title{Introducing Garage}
\subtitle{a new storage platform for self-hosted geo-distributed clusters}
\author{Deuxfleurs Association}
\date{IMT Atlantique, 2022-06-23}
\begin{document}
\begin{frame}
\centering
\includegraphics[width=.3\linewidth]{../../sticker/Garage.pdf}
\vspace{1em}
{\large\bf Deuxfleurs Association}
\vspace{1em}
\url{https://garagehq.deuxfleurs.fr/}
Matrix channel: \texttt{\#garage:deuxfleurs.fr}
\end{frame}
\begin{frame}
\frametitle{Who we are}
\begin{columns}[t]
\begin{column}{.2\textwidth}
\centering
\adjincludegraphics[width=.4\linewidth, valign=t]{assets/alex.jpg}
\end{column}
\begin{column}{.6\textwidth}
\textbf{Alex Auvolat}\\
PhD at Inria, team WIDE; co-founder of Deuxfleurs
\end{column}
\begin{column}{.2\textwidth}
~
\end{column}
\end{columns}
\vspace{1em}
\begin{columns}[t]
\begin{column}{.2\textwidth}
~
\end{column}
\begin{column}{.6\textwidth}
\textbf{Quentin Dufour}\\
PhD at Inria, team WIDE; co-founder of Deuxfleurs
\end{column}
\begin{column}{.2\textwidth}
\centering
\adjincludegraphics[width=.5\linewidth, valign=t]{assets/quentin.jpg}
\end{column}
\end{columns}
\vspace{2em}
\begin{columns}[t]
\begin{column}{.2\textwidth}
\centering
\adjincludegraphics[width=.5\linewidth, valign=t]{assets/deuxfleurs.pdf}
\end{column}
\begin{column}{.6\textwidth}
\textbf{Deuxfleurs}\\
A non-profit self-hosting collective,\\
member of the CHATONS network
\end{column}
\begin{column}{.2\textwidth}
\centering
\adjincludegraphics[width=.7\linewidth, valign=t]{assets/logo_chatons.png}
\end{column}
\end{columns}
\end{frame}
\begin{frame}
\frametitle{Our objective at Deuxfleurs}
\begin{center}
\textbf{Promote self-hosting and small-scale hosting\\
as an alternative to large cloud providers}
\end{center}
\vspace{2em}
\visible<2->{
Why is it hard?
}
\visible<3->{
\vspace{2em}
\begin{center}
\textbf{\underline{Resilience}}\\
{\footnotesize (we want good uptime/availability with low supervision)}
\end{center}
}
\end{frame}
\begin{frame}
\frametitle{How to make a \underline{stable} system}
Enterprise-grade systems typically employ:
\vspace{1em}
\begin{itemize}
\item RAID
\item Redundant power grid + UPS
\item Redundant Internet connections
\item Low-latency links
\item ...
\end{itemize}
\vspace{1em}
$\to$ it's costly and only worth it at DC scale
\end{frame}
\begin{frame}
\frametitle{How to make a \underline{resilient} system}
\only<1,4-5>{
Instead, we use:
\vspace{1em}
\begin{itemize}
\item \textcolor<2->{gray}{Commodity hardware (e.g. old desktop PCs)}
\vspace{.5em}
\item<4-> \textcolor<5->{gray}{Commodity Internet (e.g. FTTB, FTTH) and power grid}
\vspace{.5em}
\item<5-> \textcolor<6->{gray}{\textbf{Geographical redundancy} (multi-site replication)}
\end{itemize}
}
\only<2>{
\begin{center}
\includegraphics[width=.8\linewidth]{assets/atuin.jpg}
\end{center}
}
\only<3>{
\begin{center}
\includegraphics[width=.8\linewidth]{assets/neptune.jpg}
\end{center}
}
\only<6>{
\begin{center}
\includegraphics[width=.5\linewidth]{assets/inframap.jpg}
\end{center}
}
\end{frame}
\begin{frame}
\frametitle{How to make this happen}
\begin{center}
\only<1>{\includegraphics[width=.8\linewidth]{assets/slide1.png}}%
\only<2>{\includegraphics[width=.8\linewidth]{assets/slide2.png}}%
\only<3>{\includegraphics[width=.8\linewidth]{assets/slide3.png}}%
\end{center}
\end{frame}
\begin{frame}
\frametitle{Distributed file systems are slow}
File systems are complex, for example:
\vspace{1em}
\begin{itemize}
\item Concurrent modification by several processes
\vspace{1em}
\item Folder hierarchies
\vspace{1em}
\item Other requirements of the POSIX spec
\end{itemize}
\vspace{1em}
Coordination in a distributed system is costly
\vspace{1em}
Costs explode with commodity hardware / Internet connections\\
{\small (we experienced this!)}
\end{frame}
\begin{frame}
\frametitle{A simpler solution: object storage}
Only two operations:
\vspace{1em}
\begin{itemize}
\item Put an object at a key
\vspace{1em}
\item Retrieve an object from its key
\end{itemize}
\vspace{1em}
{\footnotesize (and a few others)}
\vspace{1em}
Sufficient for many applications!
\end{frame}
\begin{frame}
\frametitle{A simpler solution: object storage}
\begin{center}
\includegraphics[width=.2\linewidth]{../2020-12-02_wide-team/img/Amazon-S3.jpg}
\hspace{5em}
\includegraphics[width=.2\linewidth]{assets/minio.png}
\end{center}
\vspace{1em}
S3: a de-facto standard, many compatible applications
\vspace{1em}
MinIO is self-hostable but not suited for geo-distributed deployments
\end{frame}
\begin{frame}
\frametitle{But what is Garage, exactly?}
\textbf{Garage is a self-hosted drop-in replacement for the Amazon S3 object store}\\
\vspace{.5em}
that implements resilience through geographical redundancy on commodity hardware
\begin{center}
\includegraphics[width=.8\linewidth]{assets/garageuses.png}
\end{center}
\end{frame}
\begin{frame}
\frametitle{Overview}
\begin{center}
\only<1>{\includegraphics[width=.45\linewidth]{assets/garage2a.drawio.pdf}}%
\only<2>{\includegraphics[width=.45\linewidth]{assets/garage2b.drawio.pdf}}%
\end{center}
\end{frame}
\begin{frame}
\frametitle{Garage is \emph{location-aware}}
\begin{center}
\includegraphics[width=\linewidth]{assets/location-aware.png}
\end{center}
\vspace{2em}
Garage replicates data on different zones when possible
\end{frame}
\begin{frame}
\frametitle{Garage is \emph{location-aware}}
\begin{center}
\includegraphics[width=.8\linewidth]{assets/map.png}
\end{center}
\end{frame}
\begin{frame}
\frametitle{How to spread files over different cluster nodes?}
\textbf{Consistent hashing (DynamoDB):}
\vspace{1em}
\begin{center}
\only<1>{\includegraphics[width=.45\columnwidth]{assets/consistent_hashing_1.pdf}}%
\only<2>{\includegraphics[width=.45\columnwidth]{assets/consistent_hashing_2.pdf}}%
\only<3>{\includegraphics[width=.45\columnwidth]{assets/consistent_hashing_3.pdf}}%
\only<4>{\includegraphics[width=.45\columnwidth]{assets/consistent_hashing_4.pdf}}%
\end{center}
\end{frame}
\begin{frame}
\frametitle{How to spread files over different cluster nodes?}
\textbf{Issues with consistent hashing:}
\vspace{1em}
\begin{itemize}
\item Doesn't dispatch data based on geographical location of nodes
\vspace{1em}
\item<2-> Geographically aware adaptation, try 1:\\
data quantities not well balanced between nodes
\vspace{1em}
\item<3-> Geographically aware adaptation, try 2:\\
too many reshuffles when adding/removing nodes
\end{itemize}
\end{frame}
\begin{frame}
\frametitle{How to spread files over different cluster nodes?}
\textbf{Garage's method: build an index table}
\vspace{1em}
Realization: we can actually precompute an optimal solution
\vspace{1em}
\visible<2->{
\begin{center}
\begin{tabular}{|l|l|l|l|}
\hline
\textbf{Partition} & \textbf{Node 1} & \textbf{Node 2} & \textbf{Node 3} \\
\hline
\hline
Partition 0 & Io (jupiter) & Drosera (atuin) & Courgette (neptune) \\
\hline
Partition 1 & Datura (atuin) & Courgette (neptune) & Io (jupiter) \\
\hline
Partition 2 & Io(jupiter) & Celeri (neptune) & Drosera (atuin) \\
\hline
\hspace{1em}$\vdots$ & \hspace{1em}$\vdots$ & \hspace{1em}$\vdots$ & \hspace{1em}$\vdots$ \\
\hline
Partition 255 & Concombre (neptune) & Io (jupiter) & Drosera (atuin) \\
\hline
\end{tabular}
\end{center}
}
\vspace{1em}
\visible<3->{
The index table is built centrally using an optimal* algorithm,\\
then propagated to all nodes\\
\hfill\footnotesize *not yet optimal but will be soon
}
\end{frame}
\begin{frame}
\frametitle{Garage's internal data structures}
\centering
\includegraphics[width=.75\columnwidth]{assets/garage_tables.pdf}
\end{frame}
%\begin{frame}
% \frametitle{Garage's architecture}
% \begin{center}
% \includegraphics[width=.35\linewidth]{assets/garage.drawio.pdf}
% \end{center}
%\end{frame}
\begin{frame}
\frametitle{Garage is \emph{coordination-free}:}
\begin{itemize}
\item No Raft or Paxos
\vspace{1em}
\item Internal data types are CRDTs
\vspace{1em}
\item All nodes are equivalent (no master/leader/index node)
\end{itemize}
\vspace{2em}
$\to$ less sensitive to higher latencies between nodes
\end{frame}
\begin{frame}
\frametitle{Consistency model}
\begin{itemize}
\item Not ACID (not required by S3 spec) / not linearizable
\vspace{1em}
\item \textbf{Read-after-write consistency}\\
{\footnotesize (stronger than eventual consistency)}
\end{itemize}
\end{frame}
\begin{frame}
\frametitle{Impact on performances}
\begin{center}
\includegraphics[width=.8\linewidth]{assets/endpoint-latency-dc.png}
\end{center}
\end{frame}
\begin{frame}
\frametitle{An ever-increasing compatibility list}
\begin{center}
\includegraphics[width=.7\linewidth]{assets/compatibility.png}
\end{center}
\end{frame}
\begin{frame}
\frametitle{Further plans for Garage}
\begin{center}
\only<1>{\includegraphics[width=.8\linewidth]{assets/slideB1.png}}%
\only<2>{\includegraphics[width=.8\linewidth]{assets/slideB2.png}}%
\only<3>{\includegraphics[width=.8\linewidth]{assets/slideB3.png}}%
\end{center}
\end{frame}
\begin{frame}
\frametitle{K2V Design}
\begin{itemize}
\item A new, custom, minimal API
\vspace{1em}
\item<2-> Exposes the partitoning mechanism of Garage\\
K2V = partition key / sort key / value (like Dynamo)
\vspace{1em}
\item<3-> Coordination-free, CRDT-friendly (inspired by Riak)\\
\vspace{1em}
\item<4-> Cryptography-friendly: values are binary blobs
\end{itemize}
\end{frame}
\begin{frame}
\frametitle{Application: an e-mail storage server}
\begin{center}
\only<1>{\includegraphics[width=.9\linewidth]{assets/aerogramme.png}}%
\end{center}
\end{frame}
\begin{frame}
\frametitle{Aerogramme data model}
\begin{center}
\only<1>{\includegraphics[width=.4\linewidth]{assets/aerogramme_datatype.drawio.pdf}}%
\only<2->{\includegraphics[width=.9\linewidth]{assets/aerogramme_keys.drawio.pdf}\vspace{1em}}%
\end{center}
\visible<3->{Aerogramme encrypts all stored values for privacy\\
(Garage server administrators can't read your mail)}
\end{frame}
\begin{frame}
\frametitle{Different deployment scenarios}
\begin{center}
\only<1>{\includegraphics[width=.9\linewidth]{assets/aerogramme_components1.drawio.pdf}}%
\only<2>{\includegraphics[width=.9\linewidth]{assets/aerogramme_components2.drawio.pdf}}%
\end{center}
\end{frame}
\begin{frame}
\frametitle{A new model for building resilient software}
\begin{itemize}
\item Design a data model suited to K2V\\
{\footnotesize (see Cassandra docs on porting SQL data models to Cassandra)}
\vspace{1em}
\begin{itemize}
\item Use CRDTs or other eventually consistent data types (see e.g. Bayou)
\vspace{1em}
\item Store opaque binary blobs to provide End-to-End Encryption\\
\end{itemize}
\vspace{1em}
\item Store big blobs (files) in S3
\vspace{1em}
\item Let Garage manage sharding, replication, failover, etc.
\end{itemize}
\end{frame}
\begin{frame}
\frametitle{Research perspectives}
\begin{itemize}
\item Write about Garage's global architecture \emph{(paper in progress)}
\vspace{1em}
\item Measure and improve Garage's performances
\vspace{1em}
\item Discuss the optimal layout algorithm, provide proofs
\vspace{1em}
\item Write about our proposed architecture for (E2EE) apps over K2V+S3
\end{itemize}
\end{frame}
\begin{frame}
\frametitle{Where to find us}
\begin{center}
\includegraphics[width=.25\linewidth]{../../logo/garage_hires.png}\\
\vspace{-1em}
\url{https://garagehq.deuxfleurs.fr/}\\
\url{mailto:garagehq@deuxfleurs.fr}\\
\texttt{\#garage:deuxfleurs.fr} on Matrix
\vspace{1.5em}
\includegraphics[width=.06\linewidth]{assets/rust_logo.png}
\includegraphics[width=.13\linewidth]{assets/AGPLv3_Logo.png}
\end{center}
\end{frame}
\end{document}
%% vim: set ts=4 sw=4 tw=0 noet spelllang=en :
+8 -8
View File
@@ -3,20 +3,20 @@ rec {
* Fixed dependencies
*/
pkgsSrc = fetchTarball {
# As of 2021-10-04
url = "https://github.com/NixOS/nixpkgs/archive/b27d18a412b071f5d7991d1648cfe78ee7afe68a.tar.gz";
sha256 = "1xy9zpypqfxs5gcq5dcla4bfkhxmh5nzn9dyqkr03lqycm9wg5cr";
# As of 2022-10-13
url = "https://github.com/NixOS/nixpkgs/archive/a3073c49bc0163fea6a121c276f526837672b555.zip";
sha256 = "1bz632psfbpmicyzjb8b4265y50shylccvfm6ry6mgnv5hvz324s";
};
cargo2nixSrc = fetchGit {
# As of 2022-03-17
url = "https://github.com/superboum/cargo2nix";
ref = "dedup_propagate";
rev = "486675c67249e735dd7eb68e1b9feac9db102be7";
# As of 2022-10-18: two small patches over unstable branch, one for clippy and one to fix feature detection
url = "https://github.com/Alexis211/cargo2nix";
ref = "custom_unstable";
rev = "a7a61179b66054904ef6a195d8da736eaaa06c36";
};
/*
* Shared objects
*/
cargo2nix = import cargo2nixSrc;
cargo2nixOverlay = import "${cargo2nixSrc}/overlay";
cargo2nixOverlay = cargo2nix.overlays.default;
}
+86 -92
View File
@@ -1,9 +1,10 @@
{
system ? builtins.currentSystem,
target ? null,
target,
compiler ? "rustc",
release ? false,
git_version ? null,
features ? null,
}:
with import ./common.nix;
@@ -12,71 +13,41 @@ let
log = v: builtins.trace v v;
pkgs = import pkgsSrc {
inherit system;
${ if target == null then null else "crossSystem" } = { config = target; };
inherit system;
crossSystem = {
config = target;
isStatic = true;
};
overlays = [ cargo2nixOverlay ];
};
/*
Rust and Nix triples are not the same. Cargo2nix has a dedicated library
to convert Nix triples to Rust ones. We need this conversion as we want to
set later options linked to our (rust) target in a generic way. Not only
the triple terminology is different, but also the "roles" are named differently.
Nix uses a build/host/target terminology where Nix's "host" maps to Cargo's "target".
*/
rustTarget = log (pkgs.rustBuilder.rustLib.rustTriple pkgs.stdenv.hostPlatform);
/*
Cargo2nix is built for rustOverlay which installs Rust from Mozilla releases.
We want our own Rust to avoid incompatibilities, like we had with musl 1.2.0.
rustc was built with musl < 1.2.0 and nix shipped musl >= 1.2.0 which lead to compilation breakage.
This is fine for 64-bit platforms, but for 32-bit platforms, we need our own Rust
to avoid incompatibilities with time_t between different versions of musl
(>= 1.2.0 shipped by NixOS, < 1.2.0 with which rustc was built), which lead to compilation breakage.
So we want a Rust release that is bound to our Nix repository to avoid these problems.
See here for more info: https://musl.libc.org/time64.html
Because Cargo2nix does not support the Rust environment shipped by NixOS,
we emulate the structure of the Rust object created by rustOverlay.
In practise, rustOverlay ships rustc+cargo in a single derivation while
NixOS ships them in separate ones. We reunite them with symlinkJoin.
*/
rustChannel = {
rustc = pkgs.symlinkJoin {
name = "rust-channel";
paths = [
pkgs.rustPlatform.rust.cargo
pkgs.rustPlatform.rust.rustc
];
*/
toolchainOptions =
if target == "x86_64-unknown-linux-musl" || target == "aarch64-unknown-linux-musl" then {
rustVersion = "1.63.0";
extraRustComponents = [ "clippy" ];
} else {
rustToolchain = pkgs.symlinkJoin {
name = "rust-static-toolchain-${target}";
paths = [
pkgs.rustPlatform.rust.cargo
pkgs.rustPlatform.rust.rustc
# clippy not needed, it only runs on amd64
];
};
};
clippy = pkgs.symlinkJoin {
name = "clippy-channel";
paths = [
pkgs.rustPlatform.rust.cargo
pkgs.rustPlatform.rust.rustc
pkgs.clippy
];
};
}.${compiler};
clippyBuilder = pkgs.writeScriptBin "clippy" ''
#!${pkgs.stdenv.shell}
. ${cargo2nixSrc + "/overlay/utils.sh"}
isBuildScript=
args=("$@")
for i in "''${!args[@]}"; do
if [ "xmetadata=" = "x''${args[$i]::9}" ]; then
args[$i]=metadata=$NIX_RUST_METADATA
elif [ "x--crate-name" = "x''${args[$i]}" ] && [ "xbuild_script_" = "x''${args[$i+1]::13}" ]; then
isBuildScript=1
fi
done
if [ "$isBuildScript" ]; then
args+=($NIX_RUST_BUILD_LINK_FLAGS)
else
args+=($NIX_RUST_LINK_FLAGS)
fi
touch invoke.log
echo "''${args[@]}" >>invoke.log
exec ${rustChannel}/bin/clippy-driver --deny warnings "''${args[@]}"
'';
buildEnv = (drv: {
rustc = drv.setBuildEnv;
@@ -84,9 +55,10 @@ let
${drv.setBuildEnv or "" }
echo
echo --- BUILDING WITH CLIPPY ---
echo
echo
export RUSTC=${clippyBuilder}/bin/clippy
export NIX_RUST_BUILD_FLAGS="''${NIX_RUST_BUILD_FLAGS} --deny warnings"
export RUSTC="''${CLIPPY_DRIVER}"
'';
}.${compiler});
@@ -97,7 +69,7 @@ let
You can have a complete list of the available options by looking at the overriden object, mkcrate:
https://github.com/cargo2nix/cargo2nix/blob/master/overlay/mkcrate.nix
*/
overrides = pkgs.rustBuilder.overrides.all ++ [
packageOverrides = pkgs: pkgs.rustBuilder.overrides.all ++ [
/*
[1] We add some logic to compile our crates with clippy, it provides us many additional lints
@@ -113,15 +85,21 @@ let
As we do not want to consider the .git folder as part of the input source,
we ask the user (the CI often) to pass the value to Nix.
[4] We ship some parts of the code disabled by default by putting them behind a flag.
It speeds up the compilation (when the feature is not required) and released crates have less dependency by default (less attack surface, disk space, etc.).
But we want to ship these additional features when we release Garage.
In the end, we chose to exclude all features from debug builds while putting (all of) them in the release builds.
Currently, the only feature of Garage is kubernetes-discovery from the garage_rpc crate.
[4] We don't want libsodium-sys and zstd-sys to try to use pkgconfig to build against a system library.
However the features to do so get activated for some reason (due to a bug in cargo2nix?),
so disable them manually here.
*/
(pkgs.rustBuilder.rustLib.makeOverride {
name = "garage";
overrideAttrs = drv: {
overrideAttrs = drv:
(if git_version != null then {
/* [3] */ preConfigure = ''
${drv.preConfigure or ""}
export GIT_VERSION="${git_version}"
'';
} else {})
//
{
/* [1] */ setBuildEnv = (buildEnv drv);
/* [2] */ hardeningDisable = [ "pie" ];
};
@@ -130,9 +108,6 @@ let
(pkgs.rustBuilder.rustLib.makeOverride {
name = "garage_rpc";
overrideAttrs = drv: { /* [1] */ setBuildEnv = (buildEnv drv); };
overrideArgs = old: {
/* [4] */ features = if release then [ "kubernetes-discovery" ] else [];
};
})
(pkgs.rustBuilder.rustLib.makeOverride {
@@ -142,15 +117,7 @@ let
(pkgs.rustBuilder.rustLib.makeOverride {
name = "garage_util";
overrideAttrs = drv:
(if git_version != null then {
/* [3] */ preConfigure = ''
${drv.preConfigure or ""}
export GIT_VERSION="${git_version}"
'';
} else {})
//
{ /* [1] */ setBuildEnv = (buildEnv drv); };
overrideAttrs = drv: { /* [1] */ setBuildEnv = (buildEnv drv); };
})
(pkgs.rustBuilder.rustLib.makeOverride {
@@ -182,8 +149,43 @@ let
name = "k2v-client";
overrideAttrs = drv: { /* [1] */ setBuildEnv = (buildEnv drv); };
})
(pkgs.rustBuilder.rustLib.makeOverride {
name = "libsodium-sys";
overrideArgs = old: {
features = [ ]; /* [4] */
};
})
(pkgs.rustBuilder.rustLib.makeOverride {
name = "zstd-sys";
overrideArgs = old: {
features = [ ]; /* [4] */
};
})
];
/*
We ship some parts of the code disabled by default by putting them behind a flag.
It speeds up the compilation (when the feature is not required) and released crates have less dependency by default (less attack surface, disk space, etc.).
But we want to ship these additional features when we release Garage.
In the end, we chose to exclude all features from debug builds while putting (all of) them in the release builds.
*/
rootFeatures = if features != null then features else
([
"garage/bundled-libs"
"garage/sled"
"garage/k2v"
] ++ (if release then [
"garage/consul-discovery"
"garage/kubernetes-discovery"
"garage/metrics"
"garage/telemetry-otlp"
"garage/lmdb"
"garage/sqlite"
] else []));
packageFun = import ../Cargo.nix;
/*
@@ -204,23 +206,15 @@ let
"x86_64-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static-pie" ];
};
in
/*
The following definition is not elegant as we use a low level function of Cargo2nix
that enables us to pass our custom rustChannel object. We need this low level definition
to pass Nix's Rust toolchains instead of Mozilla's one.
NixOS and Rust/Cargo triples do not match for ARM, fix it here.
*/
rustTarget = if target == "armv6l-unknown-linux-musleabihf"
then "arm-unknown-linux-musleabihf"
else target;
target is mandatory but must be kept to null to allow cargo2nix to set it to the appropriate value
for each crate.
*/
pkgs.rustBuilder.makePackageSet {
inherit packageFun rustChannel release codegenOpts;
packageOverrides = overrides;
target = null;
buildRustPackages = pkgs.buildPackages.rustBuilder.makePackageSet {
inherit rustChannel packageFun codegenOpts;
packageOverrides = overrides;
target = null;
};
}
in
pkgs.rustBuilder.makePackageSet ({
inherit release packageFun packageOverrides codegenOpts rootFeatures;
target = rustTarget;
} // toolchainOptions)
+23
View File
@@ -0,0 +1,23 @@
pkgs:
pkgs.buildGoModule rec {
pname = "manifest-tool";
version = "2.0.5";
src = pkgs.fetchFromGitHub {
owner = "estesp";
repo = "manifest-tool";
rev = "v${version}";
sha256 = "hjCGKnE0yrlnF/VIzOwcDzmQX3Wft+21KCny/opqdLg=";
} + "/v2";
vendorSha256 = null;
checkPhase = "true";
meta = with pkgs.lib; {
description = "Command line tool to create and query container image manifest list/indexes";
homepage = "https://github.com/estesp/manifest-tool";
license = licenses.asl20;
platforms = platforms.linux;
};
}
+9 -3
View File
@@ -6,19 +6,24 @@ with import ./common.nix;
let
platforms = [
"x86_64-unknown-linux-musl"
#"x86_64-unknown-linux-musl"
"i686-unknown-linux-musl"
"aarch64-unknown-linux-musl"
#"aarch64-unknown-linux-musl"
"armv6l-unknown-linux-musleabihf"
];
pkgsList = builtins.map (target: import pkgsSrc {
inherit system;
crossSystem = { config = target; };
crossSystem = {
config = target;
isStatic = true;
};
overlays = [ cargo2nixOverlay ];
}) platforms;
pkgsHost = import pkgsSrc {};
lib = pkgsHost.lib;
kaniko = (import ./kaniko.nix) pkgsHost;
winscp = (import ./winscp.nix) pkgsHost;
manifestTool = (import ./manifest-tool.nix) pkgsHost;
in
lib.flatten (builtins.map (pkgs: [
pkgs.rustPlatform.rust.rustc
@@ -27,5 +32,6 @@ in
]) pkgsList) ++ [
kaniko
winscp
manifestTool
]
+1 -1
View File
@@ -11,7 +11,7 @@ PATH="${GARAGE_DEBUG}:${GARAGE_RELEASE}:${NIX_RELEASE}:$PATH"
FANCYCOLORS=("41m" "42m" "44m" "45m" "100m" "104m")
export RUST_BACKTRACE=1
export RUST_LOG=garage=info,garage_api=debug
export RUST_LOG=garage=info,garage_api=debug,netapp=trace
MAIN_LABEL="\e[${FANCYCOLORS[0]}[main]\e[49m"
WHICH_GARAGE=$(which garage || exit 1)
+3
View File
@@ -0,0 +1,3 @@
# Garage helm3 chart
Documentation is located [here](/doc/book/cookbook/kubernetes.md).
+23
View File
@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/
+24
View File
@@ -0,0 +1,24 @@
apiVersion: v2
name: garage
description: S3-compatible object store for small self-hosted geo-distributed deployments
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.0
# This is the version number of the application being deployed. This version number should be
# 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.7.2.1"
+88
View File
@@ -0,0 +1,88 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "garage.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "garage.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create the name of the rpc secret
*/}}
{{- define "garage.rpcSecretName" -}}
{{- printf "%s-rpc-secret" (include "garage.fullname" .) -}}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "garage.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "garage.labels" -}}
helm.sh/chart: {{ include "garage.chart" . }}
{{ include "garage.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "garage.selectorLabels" -}}
app.kubernetes.io/name: {{ include "garage.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "garage.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "garage.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
Returns given number of random Hex characters.
In practice, it generates up to 100 randAlphaNum strings
that are filtered from non-hex characters and augmented
to the resulting string that is finally trimmed down.
*/}}
{{- define "jupyterhub.randHex" -}}
{{- $result := "" }}
{{- range $i := until 100 }}
{{- if lt (len $result) . }}
{{- $rand_list := randAlphaNum . | splitList "" -}}
{{- $reduced_list := without $rand_list "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z" "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z" }}
{{- $rand_string := join "" $reduced_list }}
{{- $result = print $result $rand_string -}}
{{- end }}
{{- end }}
{{- $result | trunc . }}
{{- end }}
@@ -0,0 +1,28 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: manage-crds-{{ .Release.Namespace }}-{{ .Release.Name }}
labels:
{{- include "garage.labels" . | nindent 4 }}
rules:
- apiGroups: ["apiextensions.k8s.io"]
resources: ["customresourcedefinitions"]
verbs: ["get", "list", "watch", "create", "patch"]
- apiGroups: ["deuxfleurs.fr"]
resources: ["garagenodes"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: allow-crds-for-{{ .Release.Namespace }}-{{ .Release.Name }}
labels:
{{- include "garage.labels" . | nindent 4 }}
subjects:
- kind: ServiceAccount
name: {{ include "garage.serviceAccountName" . }}
namespace: {{ .Release.Namespace }}
roleRef:
kind: ClusterRole
name: manage-crds-{{ .Release.Namespace }}-{{ .Release.Name }}
apiGroup: rbac.authorization.k8s.io
@@ -0,0 +1,30 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "garage.fullname" . }}-config
data:
garage.toml: |-
metadata_dir = "{{ .Values.garage.metadataDir }}"
data_dir = "{{ .Values.garage.dataDir }}"
replication_mode = "{{ .Values.garage.replicationMode }}"
rpc_bind_addr = "{{ .Values.garage.rpcBindAddr }}"
# rpc_secret will be populated by the init container from a k8s secret object
rpc_secret = "__RPC_SECRET_REPLACE__"
bootstrap_peers = {{ .Values.garage.bootstrapPeers }}
kubernetes_namespace = "{{ .Release.Namespace }}"
kubernetes_service_name = "{{ include "garage.fullname" . }}"
kubernetes_skip_crd = {{ .Values.garage.kubernetesSkipCrd }}
[s3_api]
s3_region = "{{ .Values.garage.s3.api.region }}"
api_bind_addr = "[::]:3900"
root_domain = "{{ .Values.garage.s3.api.rootDomain }}"
[s3_web]
bind_addr = "[::]:3902"
root_domain = "{{ .Values.garage.s3.web.rootDomain }}"
index = "{{ .Values.garage.s3.web.index }}"
+123
View File
@@ -0,0 +1,123 @@
{{- if .Values.ingress.s3.api.enabled -}}
{{- $fullName := include "garage.fullname" . -}}
{{- $svcPort := .Values.service.s3.api.port -}}
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.ingress.s3.api.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.ingress.s3.api.annotations "kubernetes.io/ingress.class" .Values.ingress.s3.api.className}}
{{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}-s3-api
labels:
{{- include "garage.labels" . | nindent 4 }}
{{- with .Values.ingress.s3.api.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if and .Values.ingress.s3.api.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.ingress.s3.api.className }}
{{- end }}
{{- if .Values.ingress.s3.api.tls }}
tls:
{{- range .Values.ingress.s3.api.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.s3.api.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
{{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
pathType: {{ .pathType }}
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ $fullName }}
port:
number: {{ $svcPort }}
{{- else }}
serviceName: {{ $fullName }}
servicePort: {{ $svcPort }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
---
{{- if .Values.ingress.s3.web.enabled -}}
{{- $fullName := include "garage.fullname" . -}}
{{- $svcPort := .Values.service.s3.web.port -}}
{{- if and .Values.ingress.s3.web.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.ingress.s3.web.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.ingress.s3.web.annotations "kubernetes.io/ingress.class" .Values.ingress.s3.web.className}}
{{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}-s3-web
labels:
{{- include "garage.labels" . | nindent 4 }}
{{- with .Values.ingress.s3.web.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if and .Values.ingress.s3.web.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.ingress.s3.web.className }}
{{- end }}
{{- if .Values.ingress.s3.web.tls }}
tls:
{{- range .Values.ingress.s3.web.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.s3.web.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
{{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
pathType: {{ .pathType }}
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ $fullName }}
port:
number: {{ $svcPort }}
{{- else }}
serviceName: {{ $fullName }}
servicePort: {{ $svcPort }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
+14
View File
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Secret
metadata:
name: {{ include "garage.rpcSecretName" . }}
labels:
{{- include "garage.labels" . | nindent 4 }}
type: Opaque
data:
{{/* retrieve the secret data using lookup function and when not exists, return an empty dictionary / map as result */}}
{{- $prevSecret := (lookup "v1" "Secret" .Release.Namespace (include "garage.rpcSecretName" .)) | default dict }}
{{- $prevSecretData := $prevSecret.data | default dict }}
{{- $prevRpcSecret := $prevSecretData.rpcSecret | default "" | b64dec }}
{{/* Priority is: 1. from values, 2. previous value, 3. generate random */}}
rpcSecret: {{ .Values.garage.rpcSecret | default $prevRpcSecret | default (include "jupyterhub.randHex" 64) | b64enc | quote }}
+19
View File
@@ -0,0 +1,19 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "garage.fullname" . }}
labels:
{{- include "garage.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.s3.api.port }}
targetPort: 3900
protocol: TCP
name: s3-api
- port: {{ .Values.service.s3.web.port }}
targetPort: 3902
protocol: TCP
name: s3-web
selector:
{{- include "garage.selectorLabels" . | nindent 4 }}
@@ -0,0 +1,12 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "garage.serviceAccountName" . }}
labels:
{{- include "garage.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
@@ -0,0 +1,116 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ include "garage.fullname" . }}
labels:
{{- include "garage.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "garage.selectorLabels" . | nindent 6 }}
serviceName: {{ include "garage.fullname" . }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "garage.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "garage.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
initContainers:
# Copies garage.toml from configmap to temporary etc volume and replaces RPC secret placeholder
- name: {{ .Chart.Name }}-init
image: busybox:1.28
command: ["sh", "-c", "sed \"s/__RPC_SECRET_REPLACE__/$RPC_SECRET/\" /mnt/garage.toml > /mnt/etc/garage.toml"]
env:
- name: RPC_SECRET
valueFrom:
secretKeyRef:
name: {{ include "garage.rpcSecretName" . }}
key: rpcSecret
volumeMounts:
- name: configmap
mountPath: /mnt/garage.toml
subPath: garage.toml
- name: etc
mountPath: /mnt/etc
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: 3900
name: s3-api
- containerPort: 3902
name: web-api
volumeMounts:
- name: meta
mountPath: /mnt/meta
- name: data
mountPath: /mnt/data
- name: etc
mountPath: /etc/garage.toml
subPath: garage.toml
# TODO
# livenessProbe:
# httpGet:
# path: /
# port: 3900
# readinessProbe:
# httpGet:
# path: /
# port: 3900
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumes:
- name: configmap
configMap:
name: {{ include "garage.fullname" . }}-config
- name: etc
emptyDir: {}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if .Values.persistence.enabled }}
volumeClaimTemplates:
- metadata:
name: meta
spec:
accessModes: [ "ReadWriteOnce" ]
{{- if hasKey .Values.persistence.meta "storageClass" }}
storageClassName: {{ .Values.persistence.meta.storageClass | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistence.meta.size | quote }}
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
{{- if hasKey .Values.persistence.data "storageClass" }}
storageClassName: {{ .Values.persistence.data.storageClass | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistence.data.size | quote }}
{{- end }}
+142
View File
@@ -0,0 +1,142 @@
# Default values for garage.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
# Garage configuration. These values go to garage.toml
garage:
metadataDir: "/mnt/meta"
dataDir: "/mnt/data"
# Default to 3 replicas, see the replication_mode section at
# https://garagehq.deuxfleurs.fr/documentation/reference-manual/configuration/
replicationMode: "3"
rpcBindAddr: "[::]:3901"
# If not given, a random secret will be generated and stored in a Secret object
rpcSecret: ""
# This is not required if you use the integrated kubernetes discovery
bootstrapPeers: []
kubernetesSkipCrd: false
s3:
api:
region: "garage"
rootDomain: ".s3.garage.tld"
web:
rootDomain: ".web.garage.tld"
index: "index.html"
# Data persistence
persistence:
enabled: true
meta:
# storageClass: "fast-storage-class"
size: 100Mi
data:
# storageClass: "slow-storage-class"
size: 100Mi
# Number of StatefulSet replicas/garage nodes to start
replicaCount: 3
image:
repository: dxflrs/amd64_garage
# please prefer using the chart version and not this tag
tag: ""
pullPolicy: IfNotPresent
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
# Specifies whether a service account should be created
create: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
podAnnotations: {}
podSecurityContext: {}
# fsGroup: 2000
securityContext:
# The default security context is heavily restricted
# feel free to tune it to your requirements
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
service:
# You can rely on any service to expose your cluster
# - ClusterIP (+ Ingress)
# - NodePort (+ Ingress)
# - LoadBalancer
type: ClusterIP
s3:
api:
port: 3900
web:
port: 3902
# NOTE: the admin API is excluded for now as it is not consistent across nodes
ingress:
s3:
api:
enabled: true
# Rely either on the className or the annotation below but not both
# replace "nginx" by an Ingress controller
# you can find examples here https://kubernetes.io/docs/concepts/services-networking/ingress-controllers
className: "nginx"
annotations:
# kubernetes.io/ingress.class: "nginx"
# kubernetes.io/tls-acme: "true"
hosts:
- host: "s3.garage.tld" # garage S3 API endpoint
paths:
- path: /
pathType: Prefix
- host: "*.s3.garage.tld" # garage S3 API endpoint, DNS style bucket access
paths:
- path: /
pathType: Prefix
tls: []
# - secretName: my-garage-cluster-tls
# hosts:
# - kubernetes.docker.internal
web:
enabled: true
className: "nginx"
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
- host: "*.web.garage.tld" # wildcard website access with bucket name prefix
paths:
- path: /
pathType: Prefix
- host: "mywebpage.example.com" # specific bucket access with FQDN bucket
paths:
- path: /
pathType: Prefix
tls: []
# - secretName: my-garage-cluster-tls
# hosts:
# - kubernetes.docker.internal
resources: {}
# The following are indicative for a small-size deployement, for anything serious double them.
# limits:
# cpu: 100m
# memory: 1024Mi
# requests:
# cpu: 100m
# memory: 512Mi
nodeSelector: {}
tolerations: []
affinity: {}
+1 -1
View File
@@ -8,7 +8,7 @@ SCRIPT_FOLDER="`dirname \"$0\"`"
REPO_FOLDER="${SCRIPT_FOLDER}/../"
GARAGE_DEBUG="${REPO_FOLDER}/target/debug/"
GARAGE_RELEASE="${REPO_FOLDER}/target/release/"
NIX_RELEASE="${REPO_FOLDER}/result/bin/"
NIX_RELEASE="${REPO_FOLDER}/result/bin/:${REPO_FOLDER}/result-bin/bin/"
PATH="${GARAGE_DEBUG}:${GARAGE_RELEASE}:${NIX_RELEASE}:$PATH"
CMDOUT=/tmp/garage.cmd.tmp
+52 -11
View File
@@ -10,24 +10,15 @@ let
overlays = [ cargo2nixOverlay ];
};
kaniko = (import ./nix/kaniko.nix) pkgs;
manifest-tool = (import ./nix/manifest-tool.nix) pkgs;
winscp = (import ./nix/winscp.nix) pkgs;
in
{
/* --- Rust Shell ---
* Use it to compile Garage
*/
rust = pkgs.mkShell {
shellHook = ''
function refresh_toolchain {
nix copy \
--to 's3://nix?endpoint=garage.deuxfleurs.fr&region=garage&secret-key=/etc/nix/signing-key.sec' \
$(nix-store -qR \
$(nix-build --quiet --no-build-output --no-out-link nix/toolchain.nix))
}
'';
nativeBuildInputs = [
#pkgs.rustPlatform.rust.rustc
pkgs.rustPlatform.rust.cargo
@@ -66,12 +57,33 @@ function refresh_toolchain {
*/
release = pkgs.mkShell {
shellHook = ''
function refresh_toolchain {
pass show deuxfleurs/nix_priv_key > /tmp/nix-signing-key.sec
nix copy \
--to 's3://nix?endpoint=garage.deuxfleurs.fr&region=garage&secret-key=/tmp/nix-signing-key.sec' \
$(nix-store -qR \
$(nix-build --no-build-output --no-out-link nix/toolchain.nix))
rm /tmp/nix-signing-key.sec
}
function refresh_cache {
pass show deuxfleurs/nix_priv_key > /tmp/nix-signing-key.sec
for attr in clippy.amd64 test.amd64 pkgs.{amd64,i386,arm,arm64}.{debug,release}; do
echo "Updating cache for ''${attr}"
derivation=$(nix-instantiate --attr ''${attr})
nix copy \
--to 's3://nix?endpoint=garage.deuxfleurs.fr&region=garage&secret-key=/tmp/nix-signing-key.sec' \
$(nix-store -qR ''${derivation%\!bin})
done
rm /tmp/nix-signing-key.sec
}
function to_s3 {
aws \
--endpoint-url https://garage.deuxfleurs.fr \
--region garage \
s3 cp \
./result/bin/garage \
./result-bin/bin/garage \
s3://garagehq.deuxfleurs.fr/_releases/''${DRONE_TAG:-$DRONE_COMMIT}/''${TARGET}/garage
}
@@ -84,6 +96,34 @@ function to_docker {
--verbosity=debug
}
function multiarch_docker {
manifest-tool push from-spec <(cat <<EOF
image: dxflrs/garage:''${CONTAINER_TAG}
manifests:
-
image: dxflrs/arm64_garage:''${CONTAINER_TAG}
platform:
architecture: arm64
os: linux
-
image: dxflrs/amd64_garage:''${CONTAINER_TAG}
platform:
architecture: amd64
os: linux
-
image: dxflrs/386_garage:''${CONTAINER_TAG}
platform:
architecture: 386
os: linux
-
image: dxflrs/arm_garage:''${CONTAINER_TAG}
platform:
architecture: arm
os: linux
EOF
)
}
function refresh_index {
aws \
--endpoint-url https://garage.deuxfleurs.fr \
@@ -113,6 +153,7 @@ function refresh_index {
nativeBuildInputs = [
pkgs.awscli2
kaniko
manifest-tool
];
};
}
+15 -14
View File
@@ -1,6 +1,6 @@
[package]
name = "garage_api"
version = "0.7.3"
version = "0.8.0"
authors = ["Alex Auvolat <alex@adnab.me>"]
edition = "2018"
license = "AGPL-3.0"
@@ -14,30 +14,31 @@ path = "lib.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
garage_model = { version = "0.7.3", path = "../model" }
garage_table = { version = "0.7.3", path = "../table" }
garage_block = { version = "0.7.3", path = "../block" }
garage_util = { version = "0.7.3", path = "../util" }
garage_rpc = { version = "0.7.3", path = "../rpc" }
garage_model = { version = "0.8.0", path = "../model" }
garage_table = { version = "0.8.0", path = "../table" }
garage_block = { version = "0.8.0", path = "../block" }
garage_util = { version = "0.8.0", path = "../util" }
garage_rpc = { version = "0.8.0", path = "../rpc" }
async-trait = "0.1.7"
base64 = "0.13"
bytes = "1.0"
chrono = "0.4"
crypto-mac = "0.10"
crypto-common = "0.1"
err-derive = "0.3"
hex = "0.4"
hmac = "0.10"
hmac = "0.12"
idna = "0.2"
tracing = "0.1.30"
md-5 = "0.9"
md-5 = "0.10"
nom = "7.1"
sha2 = "0.9"
sha2 = "0.10"
futures = "0.3"
futures-util = "0.3"
pin-project = "1.0"
pin-project = "1.0.11"
tokio = { version = "1.0", default-features = false, features = ["rt", "rt-multi-thread", "io-util", "net", "time", "macros", "sync", "signal", "fs"] }
tokio-stream = "0.1"
form_urlencoded = "1.0.0"
http = "0.2"
@@ -54,9 +55,9 @@ quick-xml = { version = "0.21", features = [ "serialize" ] }
url = "2.1"
opentelemetry = "0.17"
opentelemetry-prometheus = "0.10"
opentelemetry-otlp = "0.10"
prometheus = "0.13"
opentelemetry-prometheus = { version = "0.10", optional = true }
prometheus = { version = "0.13", optional = true }
[features]
k2v = [ "garage_util/k2v", "garage_model/k2v" ]
metrics = [ "opentelemetry-prometheus", "prometheus" ]
+46 -36
View File
@@ -1,15 +1,17 @@
use std::net::SocketAddr;
use std::sync::Arc;
use async_trait::async_trait;
use futures::future::Future;
use http::header::{
ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN, ALLOW, CONTENT_TYPE,
};
use hyper::{Body, Request, Response};
use http::header::{ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN, ALLOW};
use hyper::{Body, Request, Response, StatusCode};
use opentelemetry::trace::{SpanRef, Tracer};
use opentelemetry::trace::SpanRef;
#[cfg(feature = "metrics")]
use opentelemetry_prometheus::PrometheusExporter;
#[cfg(feature = "metrics")]
use prometheus::{Encoder, TextEncoder};
use garage_model::garage::Garage;
@@ -25,14 +27,17 @@ use crate::admin::router::{Authorization, Endpoint};
pub struct AdminApiServer {
garage: Arc<Garage>,
#[cfg(feature = "metrics")]
exporter: PrometheusExporter,
metrics_token: Option<String>,
admin_token: Option<String>,
}
impl AdminApiServer {
pub fn new(garage: Arc<Garage>) -> Self {
let exporter = opentelemetry_prometheus::exporter().init();
pub fn new(
garage: Arc<Garage>,
#[cfg(feature = "metrics")] exporter: PrometheusExporter,
) -> Self {
let cfg = &garage.config.admin;
let metrics_token = cfg
.metrics_token
@@ -44,26 +49,27 @@ impl AdminApiServer {
.map(|tok| format!("Bearer {}", tok));
Self {
garage,
#[cfg(feature = "metrics")]
exporter,
metrics_token,
admin_token,
}
}
pub async fn run(self, shutdown_signal: impl Future<Output = ()>) -> Result<(), GarageError> {
if let Some(bind_addr) = self.garage.config.admin.api_bind_addr {
let region = self.garage.config.s3_api.s3_region.clone();
ApiServer::new(region, self)
.run_server(bind_addr, shutdown_signal)
.await
} else {
Ok(())
}
pub async fn run(
self,
bind_addr: SocketAddr,
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)
.await
}
fn handle_options(&self, _req: &Request<Body>) -> Result<Response<Body>, Error> {
Ok(Response::builder()
.status(204)
.status(StatusCode::NO_CONTENT)
.header(ALLOW, "OPTIONS, GET, POST")
.header(ACCESS_CONTROL_ALLOW_METHODS, "OPTIONS, GET, POST")
.header(ACCESS_CONTROL_ALLOW_ORIGIN, "*")
@@ -71,22 +77,31 @@ impl AdminApiServer {
}
fn handle_metrics(&self) -> Result<Response<Body>, Error> {
let mut buffer = vec![];
let encoder = TextEncoder::new();
#[cfg(feature = "metrics")]
{
use opentelemetry::trace::Tracer;
let tracer = opentelemetry::global::tracer("garage");
let metric_families = tracer.in_span("admin/gather_metrics", |_| {
self.exporter.registry().gather()
});
let mut buffer = vec![];
let encoder = TextEncoder::new();
encoder
.encode(&metric_families, &mut buffer)
.ok_or_internal_error("Could not serialize metrics")?;
let tracer = opentelemetry::global::tracer("garage");
let metric_families = tracer.in_span("admin/gather_metrics", |_| {
self.exporter.registry().gather()
});
Ok(Response::builder()
.status(200)
.header(CONTENT_TYPE, encoder.format_type())
.body(Body::from(buffer))?)
encoder
.encode(&metric_families, &mut buffer)
.ok_or_internal_error("Could not serialize metrics")?;
Ok(Response::builder()
.status(StatusCode::OK)
.header(http::header::CONTENT_TYPE, encoder.format_type())
.body(Body::from(buffer))?)
}
#[cfg(not(feature = "metrics"))]
Err(Error::bad_request(
"Garage was built without the metrics feature".to_string(),
))
}
}
@@ -156,12 +171,7 @@ impl ApiHandler for AdminApiServer {
}
Endpoint::CreateBucket => handle_create_bucket(&self.garage, req).await,
Endpoint::DeleteBucket { id } => handle_delete_bucket(&self.garage, id).await,
Endpoint::PutBucketWebsite { id } => {
handle_put_bucket_website(&self.garage, id, req).await
}
Endpoint::DeleteBucketWebsite { id } => {
handle_delete_bucket_website(&self.garage, id).await
}
Endpoint::UpdateBucket { id } => handle_update_bucket(&self.garage, id, req).await,
// Bucket-key permissions
Endpoint::BucketAllowKey => {
handle_bucket_change_key_perm(&self.garage, req, true).await

Some files were not shown because too many files have changed in this diff Show More