Compare commits
3 commits
main
...
bug/check_
Author | SHA1 | Date | |
---|---|---|---|
fe053957e5 | |||
e680efb361 | |||
4d269787b2 |
347
.drone.yml
|
@ -2,28 +2,69 @@
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: default
|
name: default
|
||||||
|
|
||||||
node:
|
workspace:
|
||||||
nix-daemon: 1
|
base: /drone/garage
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
host:
|
||||||
|
path: /var/lib/drone/nix
|
||||||
|
- name: nix_config
|
||||||
|
temp: {}
|
||||||
|
|
||||||
|
environment:
|
||||||
|
HOME: /drone/garage
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: check formatting
|
- name: setup nix
|
||||||
image: nixpkgs/nix:nixos-22.05
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
|
commands:
|
||||||
|
- cp nix/nix.conf /etc/nix/nix.conf
|
||||||
|
- nix-build --no-build-output --no-out-link shell.nix -A rust.inputDerivation -A integration.inputDerivation -A release.inputDerivation
|
||||||
|
|
||||||
|
- name: code quality
|
||||||
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
commands:
|
commands:
|
||||||
- nix-shell --attr rust --run "cargo fmt -- --check"
|
- nix-shell --attr rust --run "cargo fmt -- --check"
|
||||||
|
- nix-shell --attr rust --run "cargo clippy -- --deny warnings"
|
||||||
|
|
||||||
- name: build
|
- name: build
|
||||||
image: nixpkgs/nix:nixos-22.05
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
commands:
|
commands:
|
||||||
- nix-build --no-build-output --attr clippy.amd64 --argstr git_version ${DRONE_TAG:-$DRONE_COMMIT}
|
- nix-build --no-build-output --argstr target x86_64-unknown-linux-musl --arg release false --argstr git_version $DRONE_COMMIT
|
||||||
|
- nix-shell --attr rust --run "./script/not-dynamic.sh result/bin/garage"
|
||||||
|
|
||||||
- name: unit + func tests
|
- name: unit + func tests
|
||||||
image: nixpkgs/nix:nixos-22.05
|
image: nixpkgs/nix:nixos-21.05
|
||||||
environment:
|
environment:
|
||||||
GARAGE_TEST_INTEGRATION_EXE: result-bin/bin/garage
|
GARAGE_TEST_INTEGRATION_EXE: result/bin/garage
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
commands:
|
commands:
|
||||||
- nix-build --no-build-output --attr clippy.amd64 --argstr git_version ${DRONE_TAG:-$DRONE_COMMIT}
|
- |
|
||||||
- nix-build --no-build-output --attr test.amd64
|
nix-build \
|
||||||
- ./result/bin/garage_db-*
|
--no-build-output \
|
||||||
|
--option log-lines 100 \
|
||||||
|
--argstr target x86_64-unknown-linux-musl \
|
||||||
|
--argstr compileMode test
|
||||||
- ./result/bin/garage_api-*
|
- ./result/bin/garage_api-*
|
||||||
- ./result/bin/garage_model-*
|
- ./result/bin/garage_model-*
|
||||||
- ./result/bin/garage_rpc-*
|
- ./result/bin/garage_rpc-*
|
||||||
|
@ -32,12 +73,16 @@ steps:
|
||||||
- ./result/bin/garage_web-*
|
- ./result/bin/garage_web-*
|
||||||
- ./result/bin/garage-*
|
- ./result/bin/garage-*
|
||||||
- ./result/bin/integration-*
|
- ./result/bin/integration-*
|
||||||
- rm result
|
|
||||||
|
|
||||||
- name: integration tests
|
- name: smoke-test
|
||||||
image: nixpkgs/nix:nixos-22.05
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
commands:
|
commands:
|
||||||
- nix-build --no-build-output --attr clippy.amd64 --argstr git_version ${DRONE_TAG:-$DRONE_COMMIT}
|
- nix-build --no-build-output --argstr target x86_64-unknown-linux-musl --arg release false --argstr git_version $DRONE_COMMIT
|
||||||
- nix-shell --attr integration --run ./script/test-smoke.sh || (cat /tmp/garage.log; false)
|
- nix-shell --attr integration --run ./script/test-smoke.sh || (cat /tmp/garage.log; false)
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
|
@ -48,39 +93,79 @@ trigger:
|
||||||
- tag
|
- tag
|
||||||
- cron
|
- cron
|
||||||
|
|
||||||
|
node:
|
||||||
|
nix: 1
|
||||||
|
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: docker
|
type: docker
|
||||||
name: release-linux-amd64
|
name: release-linux-x86_64
|
||||||
|
|
||||||
node:
|
volumes:
|
||||||
nix-daemon: 1
|
- name: nix_store
|
||||||
|
host:
|
||||||
|
path: /var/lib/drone/nix
|
||||||
|
- name: nix_config
|
||||||
|
temp: {}
|
||||||
|
|
||||||
|
environment:
|
||||||
|
TARGET: x86_64-unknown-linux-musl
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: build
|
- name: setup nix
|
||||||
image: nixpkgs/nix:nixos-22.05
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
commands:
|
commands:
|
||||||
- nix-build --no-build-output --attr pkgs.amd64.release --argstr git_version ${DRONE_TAG:-$DRONE_COMMIT}
|
- cp nix/nix.conf /etc/nix/nix.conf
|
||||||
- nix-shell --attr rust --run "./script/not-dynamic.sh result-bin/bin/garage"
|
- nix-build --no-build-output --no-out-link shell.nix -A rust.inputDerivation -A integration.inputDerivation -A release.inputDerivation
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
|
commands:
|
||||||
|
- nix-build --no-build-output --argstr target $TARGET --arg release true --argstr git_version $DRONE_COMMIT
|
||||||
|
- nix-shell --attr rust --run "./script/not-dynamic.sh result/bin/garage"
|
||||||
|
|
||||||
- name: integration
|
- name: integration
|
||||||
image: nixpkgs/nix:nixos-22.05
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
commands:
|
commands:
|
||||||
- nix-shell --attr integration --run ./script/test-smoke.sh || (cat /tmp/garage.log; false)
|
- nix-shell --attr integration --run ./script/test-smoke.sh || (cat /tmp/garage.log; false)
|
||||||
|
|
||||||
- name: push static binary
|
- name: push static binary
|
||||||
image: nixpkgs/nix:nixos-22.05
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
environment:
|
environment:
|
||||||
AWS_ACCESS_KEY_ID:
|
AWS_ACCESS_KEY_ID:
|
||||||
from_secret: garagehq_aws_access_key_id
|
from_secret: garagehq_aws_access_key_id
|
||||||
AWS_SECRET_ACCESS_KEY:
|
AWS_SECRET_ACCESS_KEY:
|
||||||
from_secret: garagehq_aws_secret_access_key
|
from_secret: garagehq_aws_secret_access_key
|
||||||
TARGET: "x86_64-unknown-linux-musl"
|
|
||||||
commands:
|
commands:
|
||||||
- nix-shell --attr release --run "to_s3"
|
- nix-shell --attr release --run "to_s3"
|
||||||
|
|
||||||
- name: docker build and publish
|
- name: docker build and publish
|
||||||
image: nixpkgs/nix:nixos-22.05
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
environment:
|
environment:
|
||||||
DOCKER_AUTH:
|
DOCKER_AUTH:
|
||||||
from_secret: docker_auth
|
from_secret: docker_auth
|
||||||
|
@ -99,39 +184,79 @@ trigger:
|
||||||
- promote
|
- promote
|
||||||
- cron
|
- cron
|
||||||
|
|
||||||
|
node:
|
||||||
|
nix: 1
|
||||||
|
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: docker
|
type: docker
|
||||||
name: release-linux-i386
|
name: release-linux-i686
|
||||||
|
|
||||||
node:
|
volumes:
|
||||||
nix-daemon: 1
|
- name: nix_store
|
||||||
|
host:
|
||||||
|
path: /var/lib/drone/nix
|
||||||
|
- name: nix_config
|
||||||
|
temp: {}
|
||||||
|
|
||||||
|
environment:
|
||||||
|
TARGET: i686-unknown-linux-musl
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: build
|
- name: setup nix
|
||||||
image: nixpkgs/nix:nixos-22.05
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
commands:
|
commands:
|
||||||
- nix-build --no-build-output --attr pkgs.i386.release --argstr git_version ${DRONE_TAG:-$DRONE_COMMIT}
|
- cp nix/nix.conf /etc/nix/nix.conf
|
||||||
- nix-shell --attr rust --run "./script/not-dynamic.sh result-bin/bin/garage"
|
- nix-build --no-build-output --no-out-link shell.nix -A rust.inputDerivation -A integration.inputDerivation -A release.inputDerivation
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
|
commands:
|
||||||
|
- nix-build --no-build-output --argstr target $TARGET --arg release true --argstr git_version $DRONE_COMMIT
|
||||||
|
- nix-shell --attr rust --run "./script/not-dynamic.sh result/bin/garage"
|
||||||
|
|
||||||
- name: integration
|
- name: integration
|
||||||
image: nixpkgs/nix:nixos-22.05
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
commands:
|
commands:
|
||||||
- nix-shell --attr integration --run ./script/test-smoke.sh || (cat /tmp/garage.log; false)
|
- nix-shell --attr integration --run ./script/test-smoke.sh || (cat /tmp/garage.log; false)
|
||||||
|
|
||||||
- name: push static binary
|
- name: push static binary
|
||||||
image: nixpkgs/nix:nixos-22.05
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
environment:
|
environment:
|
||||||
AWS_ACCESS_KEY_ID:
|
AWS_ACCESS_KEY_ID:
|
||||||
from_secret: garagehq_aws_access_key_id
|
from_secret: garagehq_aws_access_key_id
|
||||||
AWS_SECRET_ACCESS_KEY:
|
AWS_SECRET_ACCESS_KEY:
|
||||||
from_secret: garagehq_aws_secret_access_key
|
from_secret: garagehq_aws_secret_access_key
|
||||||
TARGET: "i686-unknown-linux-musl"
|
|
||||||
commands:
|
commands:
|
||||||
- nix-shell --attr release --run "to_s3"
|
- nix-shell --attr release --run "to_s3"
|
||||||
|
|
||||||
- name: docker build and publish
|
- name: docker build and publish
|
||||||
image: nixpkgs/nix:nixos-22.05
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
environment:
|
environment:
|
||||||
DOCKER_AUTH:
|
DOCKER_AUTH:
|
||||||
from_secret: docker_auth
|
from_secret: docker_auth
|
||||||
|
@ -149,34 +274,69 @@ trigger:
|
||||||
- promote
|
- promote
|
||||||
- cron
|
- cron
|
||||||
|
|
||||||
|
node:
|
||||||
|
nix: 1
|
||||||
|
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: docker
|
type: docker
|
||||||
name: release-linux-arm64
|
name: release-linux-aarch64
|
||||||
|
|
||||||
node:
|
volumes:
|
||||||
nix-daemon: 1
|
- name: nix_store
|
||||||
|
host:
|
||||||
|
path: /var/lib/drone/nix
|
||||||
|
- name: nix_config
|
||||||
|
temp: {}
|
||||||
|
|
||||||
|
environment:
|
||||||
|
TARGET: aarch64-unknown-linux-musl
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: build
|
- name: setup nix
|
||||||
image: nixpkgs/nix:nixos-22.05
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
commands:
|
commands:
|
||||||
- nix-build --no-build-output --attr pkgs.arm64.release --argstr git_version ${DRONE_TAG:-$DRONE_COMMIT}
|
- cp nix/nix.conf /etc/nix/nix.conf
|
||||||
- nix-shell --attr rust --run "./script/not-dynamic.sh result-bin/bin/garage"
|
- nix-build --no-build-output --no-out-link shell.nix -A rust.inputDerivation -A integration.inputDerivation -A release.inputDerivation
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
|
commands:
|
||||||
|
- nix-build --no-build-output --argstr target $TARGET --arg release true --argstr git_version $DRONE_COMMIT
|
||||||
|
- nix-shell --attr rust --run "./script/not-dynamic.sh result/bin/garage"
|
||||||
|
|
||||||
- name: push static binary
|
- name: push static binary
|
||||||
image: nixpkgs/nix:nixos-22.05
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
environment:
|
environment:
|
||||||
AWS_ACCESS_KEY_ID:
|
AWS_ACCESS_KEY_ID:
|
||||||
from_secret: garagehq_aws_access_key_id
|
from_secret: garagehq_aws_access_key_id
|
||||||
AWS_SECRET_ACCESS_KEY:
|
AWS_SECRET_ACCESS_KEY:
|
||||||
from_secret: garagehq_aws_secret_access_key
|
from_secret: garagehq_aws_secret_access_key
|
||||||
TARGET: "aarch64-unknown-linux-musl"
|
|
||||||
commands:
|
commands:
|
||||||
- nix-shell --attr release --run "to_s3"
|
- nix-shell --attr release --run "to_s3"
|
||||||
|
|
||||||
- name: docker build and publish
|
- name: docker build and publish
|
||||||
image: nixpkgs/nix:nixos-22.05
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
environment:
|
environment:
|
||||||
DOCKER_AUTH:
|
DOCKER_AUTH:
|
||||||
from_secret: docker_auth
|
from_secret: docker_auth
|
||||||
|
@ -194,34 +354,69 @@ trigger:
|
||||||
- promote
|
- promote
|
||||||
- cron
|
- cron
|
||||||
|
|
||||||
|
node:
|
||||||
|
nix: 1
|
||||||
|
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: docker
|
type: docker
|
||||||
name: release-linux-arm
|
name: release-linux-armv6l
|
||||||
|
|
||||||
node:
|
volumes:
|
||||||
nix-daemon: 1
|
- name: nix_store
|
||||||
|
host:
|
||||||
|
path: /var/lib/drone/nix
|
||||||
|
- name: nix_config
|
||||||
|
temp: {}
|
||||||
|
|
||||||
|
environment:
|
||||||
|
TARGET: armv6l-unknown-linux-musleabihf
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: build
|
- name: setup nix
|
||||||
image: nixpkgs/nix:nixos-22.05
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
commands:
|
commands:
|
||||||
- nix-build --no-build-output --attr pkgs.arm.release --argstr git_version ${DRONE_TAG:-$DRONE_COMMIT}
|
- cp nix/nix.conf /etc/nix/nix.conf
|
||||||
- nix-shell --attr rust --run "./script/not-dynamic.sh result-bin/bin/garage"
|
- nix-build --no-build-output --no-out-link shell.nix -A rust.inputDerivation -A integration.inputDerivation -A release.inputDerivation
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
|
commands:
|
||||||
|
- nix-build --no-build-output --argstr target $TARGET --arg release true --argstr git_version $DRONE_COMMIT
|
||||||
|
- nix-shell --attr rust --run "./script/not-dynamic.sh result/bin/garage"
|
||||||
|
|
||||||
- name: push static binary
|
- name: push static binary
|
||||||
image: nixpkgs/nix:nixos-22.05
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
environment:
|
environment:
|
||||||
AWS_ACCESS_KEY_ID:
|
AWS_ACCESS_KEY_ID:
|
||||||
from_secret: garagehq_aws_access_key_id
|
from_secret: garagehq_aws_access_key_id
|
||||||
AWS_SECRET_ACCESS_KEY:
|
AWS_SECRET_ACCESS_KEY:
|
||||||
from_secret: garagehq_aws_secret_access_key
|
from_secret: garagehq_aws_secret_access_key
|
||||||
TARGET: "armv6l-unknown-linux-musleabihf"
|
|
||||||
commands:
|
commands:
|
||||||
- nix-shell --attr release --run "to_s3"
|
- nix-shell --attr release --run "to_s3"
|
||||||
|
|
||||||
- name: docker build and publish
|
- name: docker build and publish
|
||||||
image: nixpkgs/nix:nixos-22.05
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
|
- name: nix_config
|
||||||
|
path: /etc/nix
|
||||||
environment:
|
environment:
|
||||||
DOCKER_AUTH:
|
DOCKER_AUTH:
|
||||||
from_secret: docker_auth
|
from_secret: docker_auth
|
||||||
|
@ -239,28 +434,25 @@ trigger:
|
||||||
- promote
|
- promote
|
||||||
- cron
|
- cron
|
||||||
|
|
||||||
|
node:
|
||||||
|
nix: 1
|
||||||
|
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: docker
|
type: docker
|
||||||
name: refresh-release-page
|
name: refresh-release-page
|
||||||
|
|
||||||
node:
|
volumes:
|
||||||
nix-daemon: 1
|
- name: nix_store
|
||||||
|
host:
|
||||||
|
path: /var/lib/drone/nix
|
||||||
|
|
||||||
steps:
|
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
|
- name: refresh-index
|
||||||
image: nixpkgs/nix:nixos-22.05
|
image: nixpkgs/nix:nixos-21.05
|
||||||
|
volumes:
|
||||||
|
- name: nix_store
|
||||||
|
path: /nix
|
||||||
environment:
|
environment:
|
||||||
AWS_ACCESS_KEY_ID:
|
AWS_ACCESS_KEY_ID:
|
||||||
from_secret: garagehq_aws_access_key_id
|
from_secret: garagehq_aws_access_key_id
|
||||||
|
@ -271,18 +463,21 @@ steps:
|
||||||
- nix-shell --attr release --run "refresh_index"
|
- nix-shell --attr release --run "refresh_index"
|
||||||
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- release-linux-amd64
|
- release-linux-x86_64
|
||||||
- release-linux-i386
|
- release-linux-i686
|
||||||
- release-linux-arm64
|
- release-linux-aarch64
|
||||||
- release-linux-arm
|
- release-linux-armv6l
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
event:
|
event:
|
||||||
- promote
|
- promote
|
||||||
- cron
|
- cron
|
||||||
|
|
||||||
|
node:
|
||||||
|
nix: 1
|
||||||
|
|
||||||
---
|
---
|
||||||
kind: signature
|
kind: signature
|
||||||
hmac: ac09a5a8c82502f67271f93afa1e1e21ce66383b8e24a6deb26b285cc1c378ba
|
hmac: 60fad5d78c12616be848aae35703f250300abab5f2eda08eb48fe3afd6cc58c8
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
*.pdf filter=lfs diff=lfs merge=lfs -text
|
1300
Cargo.lock
generated
|
@ -1,5 +1,4 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
|
||||||
members = [
|
members = [
|
||||||
"src/db",
|
"src/db",
|
||||||
"src/util",
|
"src/util",
|
||||||
|
|
|
@ -3,5 +3,5 @@ FROM scratch
|
||||||
ENV RUST_BACKTRACE=1
|
ENV RUST_BACKTRACE=1
|
||||||
ENV RUST_LOG=garage=info
|
ENV RUST_LOG=garage=info
|
||||||
|
|
||||||
COPY result-bin/bin/garage /
|
COPY result/bin/garage /
|
||||||
CMD [ "/garage", "server"]
|
CMD [ "/garage", "server"]
|
||||||
|
|
2
Makefile
|
@ -1,7 +1,7 @@
|
||||||
.PHONY: doc all release shell run1 run2 run3
|
.PHONY: doc all release shell run1 run2 run3
|
||||||
|
|
||||||
all:
|
all:
|
||||||
clear; cargo build
|
clear; cargo build --all-features
|
||||||
|
|
||||||
release:
|
release:
|
||||||
nix-build --arg release true
|
nix-build --arg release true
|
||||||
|
|
28
README.md
|
@ -15,24 +15,18 @@ Garage [![Build Status](https://drone.deuxfleurs.fr/api/badges/Deuxfleurs/garage
|
||||||
]
|
]
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
Garage is an S3-compatible distributed object storage service
|
Garage is a lightweight S3-compatible distributed object store, with the following goals:
|
||||||
designed for self-hosting at a small-to-medium scale.
|
|
||||||
|
|
||||||
Garage is designed for storage clusters composed of nodes running
|
- As self-contained as possible
|
||||||
at different physical locations,
|
- Easy to set up
|
||||||
in order to easily provide a storage service that replicates data at these different
|
- Highly resilient to network failures, network latency, disk failures, sysadmin failures
|
||||||
locations and stays available even when some servers are unreachable.
|
- Relatively simple
|
||||||
Garage also focuses on being lightweight, easy to operate, and highly resilient to
|
- Made for multi-datacenter deployments
|
||||||
machine failures.
|
|
||||||
|
|
||||||
Garage is built by [Deuxfleurs](https://deuxfleurs.fr),
|
Non-goals include:
|
||||||
an experimental small-scale self hosted service provider,
|
|
||||||
which has been using it in production since its first release in 2020.
|
|
||||||
|
|
||||||
Learn more on our dedicated documentation pages:
|
- 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)
|
||||||
|
|
||||||
- [Goals and use cases](https://garagehq.deuxfleurs.fr/documentation/design/goals/)
|
Our main use case is to provide a distributed storage layer for small-scale self hosted services such as [Deuxfleurs](https://deuxfleurs.fr).
|
||||||
- [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.
|
|
||||||
|
|
185
default.nix
|
@ -1,62 +1,149 @@
|
||||||
{
|
{
|
||||||
system ? builtins.currentSystem,
|
system ? builtins.currentSystem,
|
||||||
|
release ? false,
|
||||||
|
target ? "x86_64-unknown-linux-musl",
|
||||||
|
compileMode ? null,
|
||||||
git_version ? null,
|
git_version ? null,
|
||||||
}:
|
}:
|
||||||
|
|
||||||
with import ./nix/common.nix;
|
with import ./nix/common.nix;
|
||||||
|
|
||||||
let
|
let
|
||||||
pkgs = import pkgsSrc { };
|
crossSystem = { config = target; };
|
||||||
compile = import ./nix/compile.nix;
|
in let
|
||||||
|
log = v: builtins.trace v v;
|
||||||
|
|
||||||
build_debug_and_release = (target: {
|
pkgs = import pkgsSrc {
|
||||||
debug = (compile {
|
inherit system crossSystem;
|
||||||
inherit system target git_version pkgsSrc cargo2nixOverlay;
|
overlays = [ cargo2nixOverlay ];
|
||||||
release = false;
|
|
||||||
}).workspace.garage {
|
|
||||||
compileMode = "build";
|
|
||||||
};
|
|
||||||
|
|
||||||
release = (compile {
|
|
||||||
inherit system target git_version pkgsSrc cargo2nixOverlay;
|
|
||||||
release = true;
|
|
||||||
}).workspace.garage {
|
|
||||||
compileMode = "build";
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
test = (rustPkgs: pkgs.symlinkJoin {
|
|
||||||
name ="garage-tests";
|
|
||||||
paths = builtins.map (key: rustPkgs.workspace.${key} { compileMode = "test"; }) (builtins.attrNames rustPkgs.workspace);
|
|
||||||
});
|
|
||||||
|
|
||||||
in {
|
|
||||||
pkgs = {
|
|
||||||
amd64 = build_debug_and_release "x86_64-unknown-linux-musl";
|
|
||||||
i386 = build_debug_and_release "i686-unknown-linux-musl";
|
|
||||||
arm64 = build_debug_and_release "aarch64-unknown-linux-musl";
|
|
||||||
arm = build_debug_and_release "armv6l-unknown-linux-musleabihf";
|
|
||||||
};
|
};
|
||||||
test = {
|
|
||||||
amd64 = test (compile {
|
|
||||||
inherit system git_version pkgsSrc cargo2nixOverlay;
|
/*
|
||||||
target = "x86_64-unknown-linux-musl";
|
Rust and Nix triples are not the same. Cargo2nix has a dedicated library
|
||||||
features = [
|
to convert Nix triples to Rust ones. We need this conversion as we want to
|
||||||
"garage/bundled-libs"
|
set later options linked to our (rust) target in a generic way. Not only
|
||||||
"garage/k2v"
|
the triple terminology is different, but also the "roles" are named differently.
|
||||||
"garage/sled"
|
Nix uses a build/host/target terminology where Nix's "host" maps to Cargo's "target".
|
||||||
"garage/lmdb"
|
*/
|
||||||
"garage/sqlite"
|
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.
|
||||||
|
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 = pkgs.symlinkJoin {
|
||||||
|
name ="rust-channel";
|
||||||
|
paths = [
|
||||||
|
pkgs.rustPlatform.rust.rustc
|
||||||
|
pkgs.rustPlatform.rust.cargo
|
||||||
|
];
|
||||||
};
|
};
|
||||||
clippy = {
|
|
||||||
amd64 = (compile {
|
/*
|
||||||
inherit system git_version pkgsSrc cargo2nixOverlay;
|
Cargo2nix provides many overrides by default, you can take inspiration from them:
|
||||||
target = "x86_64-unknown-linux-musl";
|
https://github.com/cargo2nix/cargo2nix/blob/master/overlay/overrides.nix
|
||||||
compiler = "clippy";
|
|
||||||
}).workspace.garage {
|
You can have a complete list of the available options by looking at the overriden object, mkcrate:
|
||||||
compileMode = "build";
|
https://github.com/cargo2nix/cargo2nix/blob/master/overlay/mkcrate.nix
|
||||||
|
*/
|
||||||
|
overrides = pkgs.rustBuilder.overrides.all ++ [
|
||||||
|
/*
|
||||||
|
[1] We need to alter Nix hardening to make static binaries: PIE,
|
||||||
|
Position Independent Executables seems to be supported only on amd64. Having
|
||||||
|
this flag set either 1. make our executables crash or 2. compile as dynamic on some platforms.
|
||||||
|
Here, we deactivate it. Later (find `codegenOpts`), we reactivate it for supported targets
|
||||||
|
(only amd64 curently) through the `-static-pie` flag.
|
||||||
|
PIE is a feature used by ASLR, which helps mitigate security issues.
|
||||||
|
Learn more about Nix Hardening at: https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/cc-wrapper/add-hardening.sh
|
||||||
|
*/
|
||||||
|
(pkgs.rustBuilder.rustLib.makeOverride {
|
||||||
|
name = "garage";
|
||||||
|
overrideAttrs = drv: { hardeningDisable = [ "pie" ]; };
|
||||||
|
})
|
||||||
|
|
||||||
|
(pkgs.rustBuilder.rustLib.makeOverride {
|
||||||
|
name = "garage_rpc";
|
||||||
|
|
||||||
|
/*
|
||||||
|
[2] We want to inject the git version while keeping the build deterministic.
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
overrideAttrs = drv:
|
||||||
|
(if git_version != null then {
|
||||||
|
preConfigure = ''
|
||||||
|
${drv.preConfigure or ""}
|
||||||
|
export GIT_VERSION="${git_version}"
|
||||||
|
'';
|
||||||
|
} else {});
|
||||||
|
|
||||||
|
/*
|
||||||
|
[3] 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.
|
||||||
|
*/
|
||||||
|
overrideArgs = old: {
|
||||||
|
features = if release then [ "kubernetes-discovery" ] else [];
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
packageFun = import ./Cargo.nix;
|
||||||
|
|
||||||
|
/*
|
||||||
|
We compile fully static binaries with musl to simplify deployment on most systems.
|
||||||
|
When possible, we reactivate PIE hardening (see above).
|
||||||
|
|
||||||
|
Also, if you set the RUSTFLAGS environment variable, the following parameters will
|
||||||
|
be ignored.
|
||||||
|
|
||||||
|
For more information on static builds, please refer to Rust's RFC 1721.
|
||||||
|
https://rust-lang.github.io/rfcs/1721-crt-static.html#specifying-dynamicstatic-c-runtime-linkage
|
||||||
|
*/
|
||||||
|
|
||||||
|
codegenOpts = {
|
||||||
|
"armv6l-unknown-linux-musleabihf" = [ "target-feature=+crt-static" "link-arg=-static" ]; /* compile as dynamic with static-pie */
|
||||||
|
"aarch64-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static" ]; /* segfault with static-pie */
|
||||||
|
"i686-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static" ]; /* segfault with static-pie */
|
||||||
|
"x86_64-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static-pie" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
|
||||||
|
target is mandatory but must be kept to null to allow cargo2nix to set it to the appropriate value
|
||||||
|
for each crate.
|
||||||
|
*/
|
||||||
|
rustPkgs = 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
|
||||||
|
if compileMode == "test"
|
||||||
|
then pkgs.symlinkJoin {
|
||||||
|
name ="garage-tests";
|
||||||
|
paths = builtins.map (key: rustPkgs.workspace.${key} { inherit compileMode; }) (builtins.attrNames rustPkgs.workspace);
|
||||||
|
}
|
||||||
|
else rustPkgs.workspace.garage { inherit compileMode; }
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
# Browse doc
|
|
||||||
|
|
||||||
Run in this directory:
|
|
||||||
|
|
||||||
```
|
|
||||||
python3 -m http.server
|
|
||||||
```
|
|
||||||
|
|
||||||
And open in your browser:
|
|
||||||
- http://localhost:8000/garage-admin-v0.html
|
|
||||||
|
|
||||||
# Validate doc
|
|
||||||
|
|
||||||
```
|
|
||||||
wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/6.1.0/openapi-generator-cli-6.1.0.jar -O openapi-generator-cli.jar
|
|
||||||
java -jar openapi-generator-cli.jar validate -i garage-admin-v0.yml
|
|
||||||
```
|
|
|
@ -1,59 +0,0 @@
|
||||||
/* montserrat-300 - latin */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Montserrat';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 300;
|
|
||||||
src: local(''),
|
|
||||||
url('../fonts/montserrat-v25-latin-300.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
|
||||||
url('../fonts/montserrat-v25-latin-300.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* montserrat-regular - latin */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Montserrat';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
src: local(''),
|
|
||||||
url('../fonts/montserrat-v25-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
|
||||||
url('../fonts/montserrat-v25-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* montserrat-700 - latin */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Montserrat';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 700;
|
|
||||||
src: local(''),
|
|
||||||
url('../fonts/montserrat-v25-latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
|
||||||
url('../fonts/montserrat-v25-latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
|
||||||
}
|
|
||||||
/* roboto-300 - latin */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Roboto';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 300;
|
|
||||||
src: local(''),
|
|
||||||
url('../fonts/roboto-v30-latin-300.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
|
||||||
url('../fonts/roboto-v30-latin-300.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* roboto-regular - latin */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Roboto';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
src: local(''),
|
|
||||||
url('../fonts/roboto-v30-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
|
||||||
url('../fonts/roboto-v30-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* roboto-700 - latin */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Roboto';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 700;
|
|
||||||
src: local(''),
|
|
||||||
url('../fonts/roboto-v30-latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
|
|
||||||
url('../fonts/roboto-v30-latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Garage Adminstration API v0</title>
|
|
||||||
<!-- needed for adaptive design -->
|
|
||||||
<meta charset="utf-8"/>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<link href="./css/redoc.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Redoc doesn't change outer page styles
|
|
||||||
-->
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<redoc spec-url='./garage-admin-v0.yml'></redoc>
|
|
||||||
<script src="./redoc.standalone.js"> </script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,54 +0,0 @@
|
||||||
+++
|
|
||||||
title = "Build your own app"
|
|
||||||
weight = 4
|
|
||||||
sort_by = "weight"
|
|
||||||
template = "documentation.html"
|
|
||||||
+++
|
|
||||||
|
|
||||||
Garage has many API that you can rely on to build complex applications.
|
|
||||||
In this section, we reference the existing SDKs and give some code examples.
|
|
||||||
|
|
||||||
|
|
||||||
## ⚠️ DISCLAIMER
|
|
||||||
|
|
||||||
**K2V AND ADMIN SDK ARE TECHNICAL PREVIEWS**. The following limitations apply:
|
|
||||||
- The API is not complete, some actions are possible only through the `garage` binary
|
|
||||||
- The underlying admin API is not yet stable nor complete, it can breaks at any time
|
|
||||||
- The generator configuration is currently tweaked, the library might break at any time due to a generator change
|
|
||||||
- Because the API and the library are not stable, none of them are published in a package manager (npm, pypi, etc.)
|
|
||||||
- This code has not been extensively tested, some things might not work (please report!)
|
|
||||||
|
|
||||||
To have the best experience possible, please consider:
|
|
||||||
- Make sure that the version of the library you are using is pinned (`go.sum`, `package-lock.json`, `requirements.txt`).
|
|
||||||
- Before upgrading your Garage cluster, make sure that you can find a version of this SDK that works with your targeted version and that you are able to update your own code to work with this new version of the library.
|
|
||||||
- Join our Matrix channel at `#garage:deuxfleurs.fr`, say that you are interested by this SDK, and report any friction.
|
|
||||||
- If stability is critical, mirror this repository on your own infrastructure, regenerate the SDKs and upgrade them at your own pace.
|
|
||||||
|
|
||||||
|
|
||||||
## About the APIs
|
|
||||||
|
|
||||||
Code can interact with Garage through 3 different APIs: S3, K2V, and Admin.
|
|
||||||
Each of them has a specific scope.
|
|
||||||
|
|
||||||
### S3
|
|
||||||
|
|
||||||
De-facto standard, introduced by Amazon, designed to store blobs of data.
|
|
||||||
|
|
||||||
### K2V
|
|
||||||
|
|
||||||
A simple database API similar to RiakKV or DynamoDB.
|
|
||||||
Think a key value store with some additional operations.
|
|
||||||
Its design is inspired by Distributed Hash Tables (DHT).
|
|
||||||
|
|
||||||
More information:
|
|
||||||
- [In the reference manual](@/documentation/reference-manual/k2v.md)
|
|
||||||
|
|
||||||
|
|
||||||
### Administration
|
|
||||||
|
|
||||||
Garage operations can also be automated through a REST API.
|
|
||||||
We are currently building this SDK for [Python](@/documentation/build/python.md#admin-api), [Javascript](@/documentation/build/javascript.md#administration) and [Golang](@/documentation/build/golang.md#administration).
|
|
||||||
|
|
||||||
More information:
|
|
||||||
- [In the reference manual](@/documentation/reference-manual/admin-api.md)
|
|
||||||
- [Full specifiction](https://garagehq.deuxfleurs.fr/api/garage-admin-v0.html)
|
|
|
@ -1,69 +0,0 @@
|
||||||
+++
|
|
||||||
title = "Golang"
|
|
||||||
weight = 30
|
|
||||||
+++
|
|
||||||
|
|
||||||
## S3
|
|
||||||
|
|
||||||
*Coming soon*
|
|
||||||
|
|
||||||
Some refs:
|
|
||||||
- Minio minio-go-sdk
|
|
||||||
- [Reference](https://docs.min.io/docs/golang-client-api-reference.html)
|
|
||||||
|
|
||||||
- Amazon aws-sdk-go-v2
|
|
||||||
- [Installation](https://aws.github.io/aws-sdk-go-v2/docs/getting-started/)
|
|
||||||
- [Reference](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/s3)
|
|
||||||
- [Example](https://aws.github.io/aws-sdk-go-v2/docs/code-examples/s3/putobject/)
|
|
||||||
|
|
||||||
## K2V
|
|
||||||
|
|
||||||
*Coming soon*
|
|
||||||
|
|
||||||
## Administration
|
|
||||||
|
|
||||||
Install the SDK with:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang
|
|
||||||
```
|
|
||||||
|
|
||||||
A short example:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
garage "git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// Set Host and other parameters
|
|
||||||
configuration := garage.NewConfiguration()
|
|
||||||
configuration.Host = "127.0.0.1:3903"
|
|
||||||
|
|
||||||
|
|
||||||
// We can now generate a client
|
|
||||||
client := garage.NewAPIClient(configuration)
|
|
||||||
|
|
||||||
// Authentication is handled through the context pattern
|
|
||||||
ctx := context.WithValue(context.Background(), garage.ContextAccessToken, "s3cr3t")
|
|
||||||
|
|
||||||
// Send a request
|
|
||||||
resp, r, err := client.NodesApi.GetNodes(ctx).Execute()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Error when calling `NodesApi.GetNodes``: %v\n", err)
|
|
||||||
fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process the response
|
|
||||||
fmt.Fprintf(os.Stdout, "Target hostname: %v\n", resp.KnownNodes[resp.Node].Hostname)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
See also:
|
|
||||||
- [generated doc](https://git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang)
|
|
||||||
- [examples](https://git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-generator/src/branch/main/example/golang)
|
|
|
@ -1,55 +0,0 @@
|
||||||
+++
|
|
||||||
title = "Javascript"
|
|
||||||
weight = 10
|
|
||||||
+++
|
|
||||||
|
|
||||||
## S3
|
|
||||||
|
|
||||||
*Coming soon*.
|
|
||||||
|
|
||||||
Some refs:
|
|
||||||
- Minio SDK
|
|
||||||
- [Reference](https://docs.min.io/docs/javascript-client-api-reference.html)
|
|
||||||
|
|
||||||
- Amazon aws-sdk-js
|
|
||||||
- [Installation](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/getting-started.html)
|
|
||||||
- [Reference](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html)
|
|
||||||
- [Example](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/s3-example-creating-buckets.html)
|
|
||||||
|
|
||||||
## K2V
|
|
||||||
|
|
||||||
*Coming soon*
|
|
||||||
|
|
||||||
## Administration
|
|
||||||
|
|
||||||
Install the SDK with:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install --save git+https://git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-js.git
|
|
||||||
```
|
|
||||||
|
|
||||||
A short example:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const garage = require('garage_administration_api_v0garage_v0_8_0');
|
|
||||||
|
|
||||||
const api = new garage.ApiClient("http://127.0.0.1:3903/v0");
|
|
||||||
api.authentications['bearerAuth'].accessToken = "s3cr3t";
|
|
||||||
|
|
||||||
const [node, layout, key, bucket] = [
|
|
||||||
new garage.NodesApi(api),
|
|
||||||
new garage.LayoutApi(api),
|
|
||||||
new garage.KeyApi(api),
|
|
||||||
new garage.BucketApi(api),
|
|
||||||
];
|
|
||||||
|
|
||||||
node.getNodes().then((data) => {
|
|
||||||
console.log(`nodes: ${Object.values(data.knownNodes).map(n => n.hostname)}`)
|
|
||||||
}, (error) => {
|
|
||||||
console.error(error);
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
See also:
|
|
||||||
- [sdk repository](https://git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-js)
|
|
||||||
- [examples](https://git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-generator/src/branch/main/example/javascript)
|
|
|
@ -1,138 +0,0 @@
|
||||||
+++
|
|
||||||
title = "Python"
|
|
||||||
weight = 20
|
|
||||||
+++
|
|
||||||
|
|
||||||
## S3
|
|
||||||
|
|
||||||
### Using Minio SDK
|
|
||||||
|
|
||||||
First install the SDK:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip3 install minio
|
|
||||||
```
|
|
||||||
|
|
||||||
Then instantiate a client object using garage root domain, api key and secret:
|
|
||||||
|
|
||||||
```python
|
|
||||||
import minio
|
|
||||||
|
|
||||||
client = minio.Minio(
|
|
||||||
"your.domain.tld",
|
|
||||||
"GKyourapikey",
|
|
||||||
"abcd[...]1234",
|
|
||||||
# Force the region, this is specific to garage
|
|
||||||
region="region",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
Then use all the standard S3 endpoints as implemented by the Minio SDK:
|
|
||||||
|
|
||||||
```
|
|
||||||
# List buckets
|
|
||||||
print(client.list_buckets())
|
|
||||||
|
|
||||||
# Put an object containing 'content' to /path in bucket named 'bucket':
|
|
||||||
content = b"content"
|
|
||||||
client.put_object(
|
|
||||||
"bucket",
|
|
||||||
"path",
|
|
||||||
io.BytesIO(content),
|
|
||||||
len(content),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Read the object back and check contents
|
|
||||||
data = client.get_object("bucket", "path").read()
|
|
||||||
assert data == content
|
|
||||||
```
|
|
||||||
|
|
||||||
For further documentation, see the Minio SDK
|
|
||||||
[Reference](https://docs.min.io/docs/python-client-api-reference.html)
|
|
||||||
|
|
||||||
### Using Amazon boto3
|
|
||||||
|
|
||||||
*Coming soon*
|
|
||||||
|
|
||||||
See the official documentation:
|
|
||||||
- [Installation](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html)
|
|
||||||
- [Reference](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html)
|
|
||||||
- [Example](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/s3-uploading-files.html)
|
|
||||||
|
|
||||||
## K2V
|
|
||||||
|
|
||||||
*Coming soon*
|
|
||||||
|
|
||||||
## Admin API
|
|
||||||
|
|
||||||
You need at least Python 3.6, pip, and setuptools.
|
|
||||||
Because the python package is in a subfolder, the command is a bit more complicated than usual:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip3 install --user 'git+https://git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-python'
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, let imagine you have a fresh Garage instance running on localhost, with the admin API configured on port 3903 with the bearer `s3cr3t`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
import garage_admin_sdk
|
|
||||||
from garage_admin_sdk.apis import *
|
|
||||||
from garage_admin_sdk.models import *
|
|
||||||
|
|
||||||
configuration = garage_admin_sdk.Configuration(
|
|
||||||
host = "http://localhost:3903/v0",
|
|
||||||
access_token = "s3cr3t"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Init APIs
|
|
||||||
api = garage_admin_sdk.ApiClient(configuration)
|
|
||||||
nodes, layout, keys, buckets = NodesApi(api), LayoutApi(api), KeyApi(api), BucketApi(api)
|
|
||||||
|
|
||||||
# Display some info on the node
|
|
||||||
status = nodes.get_nodes()
|
|
||||||
print(f"running garage {status.garage_version}, node_id {status.node}")
|
|
||||||
|
|
||||||
# Change layout of this node
|
|
||||||
current = layout.get_layout()
|
|
||||||
layout.add_layout({
|
|
||||||
status.node: NodeClusterInfo(
|
|
||||||
zone = "dc1",
|
|
||||||
capacity = 1,
|
|
||||||
tags = [ "dev" ],
|
|
||||||
)
|
|
||||||
})
|
|
||||||
layout.apply_layout(LayoutVersion(
|
|
||||||
version = current.version + 1
|
|
||||||
))
|
|
||||||
|
|
||||||
# Create key, allow it to create buckets
|
|
||||||
kinfo = keys.add_key(AddKeyRequest(name="openapi"))
|
|
||||||
|
|
||||||
allow_create = UpdateKeyRequestAllow(create_bucket=True)
|
|
||||||
keys.update_key(kinfo.access_key_id, UpdateKeyRequest(allow=allow_create))
|
|
||||||
|
|
||||||
# Create a bucket, allow key, set quotas
|
|
||||||
binfo = buckets.create_bucket(CreateBucketRequest(global_alias="documentation"))
|
|
||||||
binfo = buckets.allow_bucket_key(AllowBucketKeyRequest(
|
|
||||||
bucket_id=binfo.id,
|
|
||||||
access_key_id=kinfo.access_key_id,
|
|
||||||
permissions=AllowBucketKeyRequestPermissions(read=True, write=True, owner=True),
|
|
||||||
))
|
|
||||||
binfo = buckets.update_bucket(binfo.id, UpdateBucketRequest(
|
|
||||||
quotas=UpdateBucketRequestQuotas(max_size=19029801,max_objects=1500)))
|
|
||||||
|
|
||||||
# Display key
|
|
||||||
print(f"""
|
|
||||||
cluster ready
|
|
||||||
key id is {kinfo.access_key_id}
|
|
||||||
secret key is {kinfo.secret_access_key}
|
|
||||||
bucket {binfo.global_aliases[0]} contains {binfo.objects}/{binfo.quotas.max_objects} objects
|
|
||||||
""")
|
|
||||||
```
|
|
||||||
|
|
||||||
*This example is named `short.py` in the example folder. Other python examples are also available.*
|
|
||||||
|
|
||||||
See also:
|
|
||||||
- [sdk repo](https://git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-python)
|
|
||||||
- [examples](https://git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-generator/src/branch/main/example/python)
|
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
+++
|
|
||||||
title = "Rust"
|
|
||||||
weight = 40
|
|
||||||
+++
|
|
||||||
|
|
||||||
## S3
|
|
||||||
|
|
||||||
*Coming soon*
|
|
||||||
|
|
||||||
Some refs:
|
|
||||||
- Amazon aws-rust-sdk
|
|
||||||
- [Github](https://github.com/awslabs/aws-sdk-rust)
|
|
||||||
|
|
||||||
## K2V
|
|
||||||
|
|
||||||
*Coming soon*
|
|
||||||
|
|
||||||
Some refs: https://git.deuxfleurs.fr/Deuxfleurs/garage/src/branch/main/src/k2v-client
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# all these values can be provided on the cli instead
|
|
||||||
export AWS_ACCESS_KEY_ID=GK123456
|
|
||||||
export AWS_SECRET_ACCESS_KEY=0123..789
|
|
||||||
export AWS_REGION=garage
|
|
||||||
export K2V_ENDPOINT=http://172.30.2.1:3903
|
|
||||||
export K2V_BUCKET=my-bucket
|
|
||||||
|
|
||||||
cargo run --features=cli -- read-range my-partition-key --all
|
|
||||||
|
|
||||||
cargo run --features=cli -- insert my-partition-key my-sort-key --text "my string1"
|
|
||||||
cargo run --features=cli -- insert my-partition-key my-sort-key --text "my string2"
|
|
||||||
cargo run --features=cli -- insert my-partition-key my-sort-key2 --text "my string"
|
|
||||||
|
|
||||||
cargo run --features=cli -- read-range my-partition-key --all
|
|
||||||
|
|
||||||
causality=$(cargo run --features=cli -- read my-partition-key my-sort-key2 -b | head -n1)
|
|
||||||
cargo run --features=cli -- delete my-partition-key my-sort-key2 -c $causality
|
|
||||||
|
|
||||||
causality=$(cargo run --features=cli -- read my-partition-key my-sort-key -b | head -n1)
|
|
||||||
cargo run --features=cli -- insert my-partition-key my-sort-key --text "my string3" -c $causality
|
|
||||||
|
|
||||||
cargo run --features=cli -- read-range my-partition-key --all
|
|
||||||
```
|
|
||||||
|
|
||||||
## Admin API
|
|
||||||
|
|
||||||
*Coming soon*
|
|
|
@ -1,5 +1,5 @@
|
||||||
+++
|
+++
|
||||||
title = "Existing integrations"
|
title = "Integrations"
|
||||||
weight = 3
|
weight = 3
|
||||||
sort_by = "weight"
|
sort_by = "weight"
|
||||||
template = "documentation.html"
|
template = "documentation.html"
|
||||||
|
@ -14,6 +14,7 @@ In particular, you will find here instructions to connect it with:
|
||||||
- [Applications](@/documentation/connect/apps/index.md)
|
- [Applications](@/documentation/connect/apps/index.md)
|
||||||
- [Website hosting](@/documentation/connect/websites.md)
|
- [Website hosting](@/documentation/connect/websites.md)
|
||||||
- [Software repositories](@/documentation/connect/repositories.md)
|
- [Software repositories](@/documentation/connect/repositories.md)
|
||||||
|
- [Your own code](@/documentation/connect/code.md)
|
||||||
- [FUSE](@/documentation/connect/fs.md)
|
- [FUSE](@/documentation/connect/fs.md)
|
||||||
|
|
||||||
### Generic instructions
|
### Generic instructions
|
||||||
|
|
|
@ -8,8 +8,8 @@ In this section, we cover the following web applications:
|
||||||
| Name | Status | Note |
|
| Name | Status | Note |
|
||||||
|------|--------|------|
|
|------|--------|------|
|
||||||
| [Nextcloud](#nextcloud) | ✅ | Both Primary Storage and External Storage are supported |
|
| [Nextcloud](#nextcloud) | ✅ | Both Primary Storage and External Storage are supported |
|
||||||
| [Peertube](#peertube) | ✅ | Supported with the website endpoint, proxifying private videos unsupported |
|
| [Peertube](#peertube) | ✅ | Must be configured with the website endpoint |
|
||||||
| [Mastodon](#mastodon) | ✅ | Natively supported |
|
| [Mastodon](#mastodon) | ❓ | Not yet tested |
|
||||||
| [Matrix](#matrix) | ✅ | Tested with `synapse-s3-storage-provider` |
|
| [Matrix](#matrix) | ✅ | Tested with `synapse-s3-storage-provider` |
|
||||||
| [Pixelfed](#pixelfed) | ❓ | Not yet tested |
|
| [Pixelfed](#pixelfed) | ❓ | Not yet tested |
|
||||||
| [Pleroma](#pleroma) | ❓ | Not yet tested |
|
| [Pleroma](#pleroma) | ❓ | Not yet tested |
|
||||||
|
@ -128,10 +128,6 @@ In other words, Peertube is only responsible of the "control plane" and offload
|
||||||
In return, this system is a bit harder to configure.
|
In return, this system is a bit harder to configure.
|
||||||
We show how it is still possible to configure Garage with Peertube, allowing you to spread the load and the bandwidth usage on the Garage cluster.
|
We show how it is still possible to configure Garage with Peertube, allowing you to spread the load and the bandwidth usage on the Garage cluster.
|
||||||
|
|
||||||
Starting from version 5.0, Peertube also supports improving the security for private videos by not exposing them directly
|
|
||||||
but relying on a single control point in the Peertube instance. This is based on S3 per-object and prefix ACL, which are not currently supported
|
|
||||||
in Garage, so this feature is unsupported. While this technically impedes security for private videos, it is not a blocking issue and could be
|
|
||||||
a reasonable trade-off for some instances.
|
|
||||||
|
|
||||||
### Create resources in Garage
|
### Create resources in Garage
|
||||||
|
|
||||||
|
@ -199,11 +195,6 @@ object_storage:
|
||||||
|
|
||||||
max_upload_part: 2GB
|
max_upload_part: 2GB
|
||||||
|
|
||||||
proxy:
|
|
||||||
# You may enable this feature, yet it will not provide any security benefit, so
|
|
||||||
# you should rather benefit from Garage public endpoint for all videos
|
|
||||||
proxify_private_files: false
|
|
||||||
|
|
||||||
streaming_playlists:
|
streaming_playlists:
|
||||||
bucket_name: 'peertube-playlist'
|
bucket_name: 'peertube-playlist'
|
||||||
|
|
||||||
|
@ -233,135 +224,7 @@ You can now reload the page and see in your browser console that data are fetche
|
||||||
|
|
||||||
## Mastodon
|
## Mastodon
|
||||||
|
|
||||||
Mastodon natively supports the S3 protocol to store media files, and it works out-of-the-box with Garage.
|
https://docs.joinmastodon.org/admin/config/#cdn
|
||||||
You will need to expose your Garage bucket as a website: that way, media files will be served directly from Garage.
|
|
||||||
|
|
||||||
### Performance considerations
|
|
||||||
|
|
||||||
Mastodon tends to store many small objects over time: expect hundreds of thousands of objects,
|
|
||||||
with average object size ranging from 50 KB to 150 KB.
|
|
||||||
|
|
||||||
As such, your Garage cluster should be configured appropriately for good performance:
|
|
||||||
|
|
||||||
- use Garage v0.8.0 or higher with the [LMDB database engine](@documentation/reference-manual/configuration.md#db-engine-since-v0-8-0).
|
|
||||||
With the default Sled database engine, your database could quickly end up taking tens of GB of disk space.
|
|
||||||
- the Garage database should be stored on a SSD
|
|
||||||
|
|
||||||
### Creating your bucket
|
|
||||||
|
|
||||||
This is the usual Garage setup:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
garage key new --name mastodon-key
|
|
||||||
garage bucket create mastodon-data
|
|
||||||
garage bucket allow mastodon-data --read --write --key mastodon-key
|
|
||||||
```
|
|
||||||
|
|
||||||
Note the Key ID and Secret Key.
|
|
||||||
|
|
||||||
### Exposing your bucket as a website
|
|
||||||
|
|
||||||
Create a DNS name to serve your media files, such as `my-social-media.mydomain.tld`.
|
|
||||||
This name will be publicly exposed to the users of your Mastodon instance: they
|
|
||||||
will load images directly from this DNS name.
|
|
||||||
|
|
||||||
As [documented here](@/documentation/cookbook/exposing-websites.md),
|
|
||||||
add this DNS name as alias to your bucket, and expose it as a website:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
garage bucket alias mastodon-data my-social-media.mydomain.tld
|
|
||||||
garage bucket website --allow mastodon-data
|
|
||||||
```
|
|
||||||
|
|
||||||
Then you will likely need to [setup a reverse proxy](@/documentation/cookbook/reverse-proxy.md)
|
|
||||||
in front of it to serve your media files over HTTPS.
|
|
||||||
|
|
||||||
### Cleaning up old media files before migration
|
|
||||||
|
|
||||||
Mastodon instance quickly accumulate a lot of media files from the federation.
|
|
||||||
Most of them are not strictly necessary because they can be fetched again from
|
|
||||||
other servers. As such, it is highly recommended to clean them up before
|
|
||||||
migration, this will greatly reduce the migration time.
|
|
||||||
|
|
||||||
From the [official Mastodon documentation](https://docs.joinmastodon.org/admin/tootctl/#media):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ RAILS_ENV=production bin/tootctl media remove --days 3
|
|
||||||
$ RAILS_ENV=production bin/tootctl media remove-orphans
|
|
||||||
$ RAILS_ENV=production bin/tootctl preview_cards remove --days 15
|
|
||||||
```
|
|
||||||
|
|
||||||
Here is a typical disk usage for a small but multi-year instance after cleanup:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ RAILS_ENV=production bin/tootctl media usage
|
|
||||||
Attachments: 5.67 GB (1.14 GB local)
|
|
||||||
Custom emoji: 295 MB (0 Bytes local)
|
|
||||||
Preview cards: 154 MB
|
|
||||||
Avatars: 3.77 GB (127 KB local)
|
|
||||||
Headers: 8.72 GB (242 KB local)
|
|
||||||
Backups: 0 Bytes
|
|
||||||
Imports: 1.7 KB
|
|
||||||
Settings: 0 Bytes
|
|
||||||
```
|
|
||||||
|
|
||||||
Unfortunately, [old avatars and headers cannot currently be cleaned up](https://github.com/mastodon/mastodon/issues/9567).
|
|
||||||
|
|
||||||
### Migrating your data
|
|
||||||
|
|
||||||
Data migration should be done with an efficient S3 client.
|
|
||||||
The [minio client](@documentation/connect/cli.md#minio-client) is a good choice
|
|
||||||
thanks to its mirror mode:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mc mirror ./public/system/ garage/mastodon-data
|
|
||||||
```
|
|
||||||
|
|
||||||
Here is a typical bucket usage after all data has been migrated:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ garage bucket info mastodon-data
|
|
||||||
|
|
||||||
Size: 20.3 GiB (21.8 GB)
|
|
||||||
Objects: 175968
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configuring Mastodon
|
|
||||||
|
|
||||||
In your `.env.production` configuration file:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
S3_ENABLED=true
|
|
||||||
# Internal access to Garage
|
|
||||||
S3_ENDPOINT=http://my-garage-instance.mydomain.tld:3900
|
|
||||||
S3_REGION=garage
|
|
||||||
S3_BUCKET=mastodon-data
|
|
||||||
# Change this (Key ID and Secret Key of your Garage key)
|
|
||||||
AWS_ACCESS_KEY_ID=GKe88df__CHANGETHIS__c5145
|
|
||||||
AWS_SECRET_ACCESS_KEY=a2f7__CHANGETHIS__77fcfcf7a58f47a4aa4431f2e675c56da37821a1070000
|
|
||||||
# What name gets exposed to users (HTTPS is implicit)
|
|
||||||
S3_ALIAS_HOST=my-social-media.mydomain.tld
|
|
||||||
```
|
|
||||||
|
|
||||||
For more details, see the [reference Mastodon documentation](https://docs.joinmastodon.org/admin/config/#cdn).
|
|
||||||
|
|
||||||
Restart all Mastodon services and everything should now be using Garage!
|
|
||||||
You can check the URLs of images in the Mastodon web client, they should start
|
|
||||||
with `https://my-social-media.mydomain.tld`.
|
|
||||||
|
|
||||||
### Last migration sync
|
|
||||||
|
|
||||||
After Mastodon is successfully using Garage, you can run a last sync from the local filesystem to Garage:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mc mirror --newer-than "3h" ./public/system/ garage/mastodon-data
|
|
||||||
```
|
|
||||||
|
|
||||||
### References
|
|
||||||
|
|
||||||
[cybrespace's guide to migrate to S3](https://github.com/cybrespace/cybrespace-meta/blob/master/s3.md)
|
|
||||||
(the guide is for Amazon S3, so the configuration is a bit different, but the rest is similar)
|
|
||||||
|
|
||||||
|
|
||||||
## Matrix
|
## Matrix
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
+++
|
+++
|
||||||
title = "Others"
|
title = "Your code (PHP, JS, Go...)"
|
||||||
weight = 99
|
weight = 30
|
||||||
+++
|
+++
|
||||||
|
|
||||||
## S3
|
|
||||||
|
|
||||||
If you are developping a new application, you may want to use Garage to store your user's media.
|
If you are developping a new application, you may want to use Garage to store your user's media.
|
||||||
|
|
||||||
The S3 API that Garage uses is a standard REST API, so as long as you can make HTTP requests,
|
The S3 API that Garage uses is a standard REST API, so as long as you can make HTTP requests,
|
||||||
|
@ -15,14 +13,44 @@ Instead, there are some libraries already avalaible.
|
||||||
|
|
||||||
Some of them are maintained by Amazon, some by Minio, others by the community.
|
Some of them are maintained by Amazon, some by Minio, others by the community.
|
||||||
|
|
||||||
### PHP
|
## PHP
|
||||||
|
|
||||||
- Amazon aws-sdk-php
|
- Amazon aws-sdk-php
|
||||||
- [Installation](https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/getting-started_installation.html)
|
- [Installation](https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/getting-started_installation.html)
|
||||||
- [Reference](https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-s3-2006-03-01.html)
|
- [Reference](https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-s3-2006-03-01.html)
|
||||||
- [Example](https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/s3-examples-creating-buckets.html)
|
- [Example](https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/s3-examples-creating-buckets.html)
|
||||||
|
|
||||||
### Java
|
## Javascript
|
||||||
|
|
||||||
|
- Minio SDK
|
||||||
|
- [Reference](https://docs.min.io/docs/javascript-client-api-reference.html)
|
||||||
|
|
||||||
|
- Amazon aws-sdk-js
|
||||||
|
- [Installation](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/getting-started.html)
|
||||||
|
- [Reference](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html)
|
||||||
|
- [Example](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/s3-example-creating-buckets.html)
|
||||||
|
|
||||||
|
## Golang
|
||||||
|
|
||||||
|
- Minio minio-go-sdk
|
||||||
|
- [Reference](https://docs.min.io/docs/golang-client-api-reference.html)
|
||||||
|
|
||||||
|
- Amazon aws-sdk-go-v2
|
||||||
|
- [Installation](https://aws.github.io/aws-sdk-go-v2/docs/getting-started/)
|
||||||
|
- [Reference](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/s3)
|
||||||
|
- [Example](https://aws.github.io/aws-sdk-go-v2/docs/code-examples/s3/putobject/)
|
||||||
|
|
||||||
|
## Python
|
||||||
|
|
||||||
|
- Minio SDK
|
||||||
|
- [Reference](https://docs.min.io/docs/python-client-api-reference.html)
|
||||||
|
|
||||||
|
- Amazon boto3
|
||||||
|
- [Installation](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html)
|
||||||
|
- [Reference](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html)
|
||||||
|
- [Example](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/s3-uploading-files.html)
|
||||||
|
|
||||||
|
## Java
|
||||||
|
|
||||||
- Minio SDK
|
- Minio SDK
|
||||||
- [Reference](https://docs.min.io/docs/java-client-api-reference.html)
|
- [Reference](https://docs.min.io/docs/java-client-api-reference.html)
|
||||||
|
@ -32,18 +60,23 @@ Some of them are maintained by Amazon, some by Minio, others by the community.
|
||||||
- [Reference](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html)
|
- [Reference](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html)
|
||||||
- [Example](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/examples-s3-objects.html)
|
- [Example](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/examples-s3-objects.html)
|
||||||
|
|
||||||
### .NET
|
## Rust
|
||||||
|
|
||||||
|
- Amazon aws-rust-sdk
|
||||||
|
- [Github](https://github.com/awslabs/aws-sdk-rust)
|
||||||
|
|
||||||
|
## .NET
|
||||||
|
|
||||||
- Minio SDK
|
- Minio SDK
|
||||||
- [Reference](https://docs.min.io/docs/dotnet-client-api-reference.html)
|
- [Reference](https://docs.min.io/docs/dotnet-client-api-reference.html)
|
||||||
|
|
||||||
- Amazon aws-dotnet-sdk
|
- Amazon aws-dotnet-sdk
|
||||||
|
|
||||||
### C++
|
## C++
|
||||||
|
|
||||||
- Amazon aws-cpp-sdk
|
- Amazon aws-cpp-sdk
|
||||||
|
|
||||||
### Haskell
|
## Haskell
|
||||||
|
|
||||||
- Minio SDK
|
- Minio SDK
|
||||||
- [Reference](https://docs.min.io/docs/haskell-client-api-reference.html)
|
- [Reference](https://docs.min.io/docs/haskell-client-api-reference.html)
|
|
@ -5,14 +5,12 @@ weight = 25
|
||||||
|
|
||||||
## Configuring a bucket for website access
|
## Configuring a bucket for website access
|
||||||
|
|
||||||
There are three methods to expose buckets as website:
|
There are two 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
|
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
|
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.
|
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.
|
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.
|
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.
|
||||||
|
|
|
@ -20,76 +20,40 @@ sudo apt-get update
|
||||||
sudo apt-get install build-essential
|
sudo apt-get install build-essential
|
||||||
```
|
```
|
||||||
|
|
||||||
## Building from source from the Gitea repository
|
## 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
|
||||||
|
|
||||||
The primary location for Garage's source code is the
|
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 enter it as follows:
|
Clone the repository and build Garage with the following commands:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://git.deuxfleurs.fr/Deuxfleurs/garage.git
|
git clone https://git.deuxfleurs.fr/Deuxfleurs/garage.git
|
||||||
cd garage
|
cd garage
|
||||||
|
cargo build
|
||||||
```
|
```
|
||||||
|
|
||||||
If you wish to build a specific version of Garage, check out the corresponding tag. For instance:
|
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).
|
||||||
|
|
||||||
```bash
|
The binaries built this way are found in `target/{debug,release}/garage`.
|
||||||
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 |
|
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
+++
|
|
||||||
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.
|
|
|
@ -1,306 +0,0 @@
|
||||||
+++
|
|
||||||
title = "Monitoring Garage"
|
|
||||||
weight = 40
|
|
||||||
+++
|
|
||||||
|
|
||||||
Garage exposes some internal metrics in the Prometheus data format.
|
|
||||||
This page explains how to exploit these metrics.
|
|
||||||
|
|
||||||
## Setting up monitoring
|
|
||||||
|
|
||||||
### Enabling the Admin API endpoint
|
|
||||||
|
|
||||||
If you have not already enabled the [administration API endpoint](@/documentation/reference-manual/admin-api.md), do so by adding the following lines to your configuration file:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[admin]
|
|
||||||
api_bind_addr = "0.0.0.0:3903"
|
|
||||||
```
|
|
||||||
|
|
||||||
This will allow anyone to scrape Prometheus metrics by fetching
|
|
||||||
`http://localhost:3093/metrics`. If you want to restrict access
|
|
||||||
to the exported metrics, set the `metrics_token` configuration value
|
|
||||||
to a bearer token to be used when fetching the metrics endpoint.
|
|
||||||
|
|
||||||
### Setting up Prometheus and Grafana
|
|
||||||
|
|
||||||
Add a scrape config to your Prometheus daemon to scrape metrics from
|
|
||||||
all of your nodes:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
scrape_configs:
|
|
||||||
- job_name: 'garage'
|
|
||||||
static_configs:
|
|
||||||
- targets:
|
|
||||||
- 'node1.mycluster:3903'
|
|
||||||
- 'node2.mycluster:3903'
|
|
||||||
- 'node3.mycluster:3903'
|
|
||||||
```
|
|
||||||
|
|
||||||
If you have set a metrics token in your Garage configuration file,
|
|
||||||
add the following lines in your Prometheus scrape config:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
authorization:
|
|
||||||
type: Bearer
|
|
||||||
credentials: 'your metrics token'
|
|
||||||
```
|
|
||||||
|
|
||||||
To visualize the scraped data in Grafana,
|
|
||||||
you can either import our [Grafana dashboard for Garage](https://git.deuxfleurs.fr/Deuxfleurs/garage/raw/branch/main/script/telemetry/grafana-garage-dashboard-prometheus.json)
|
|
||||||
or make your own.
|
|
||||||
We detail below the list of exposed metrics and their meaning.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## List of exported metrics
|
|
||||||
|
|
||||||
|
|
||||||
### Metrics of the API endpoints
|
|
||||||
|
|
||||||
#### `api_admin_request_counter` (counter)
|
|
||||||
|
|
||||||
Counts the number of requests to a given endpoint of the administration API. Example:
|
|
||||||
|
|
||||||
```
|
|
||||||
api_admin_request_counter{api_endpoint="Metrics"} 127041
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `api_admin_request_duration` (histogram)
|
|
||||||
|
|
||||||
Evaluates the duration of API calls to the various administration API endpoint. Example:
|
|
||||||
|
|
||||||
```
|
|
||||||
api_admin_request_duration_bucket{api_endpoint="Metrics",le="0.5"} 127041
|
|
||||||
api_admin_request_duration_sum{api_endpoint="Metrics"} 605.250344830999
|
|
||||||
api_admin_request_duration_count{api_endpoint="Metrics"} 127041
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `api_s3_request_counter` (counter)
|
|
||||||
|
|
||||||
Counts the number of requests to a given endpoint of the S3 API. Example:
|
|
||||||
|
|
||||||
```
|
|
||||||
api_s3_request_counter{api_endpoint="CreateMultipartUpload"} 1
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `api_s3_error_counter` (counter)
|
|
||||||
|
|
||||||
Counts the number of requests to a given endpoint of the S3 API that returned an error. Example:
|
|
||||||
|
|
||||||
```
|
|
||||||
api_s3_error_counter{api_endpoint="GetObject",status_code="404"} 39
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `api_s3_request_duration` (histogram)
|
|
||||||
|
|
||||||
Evaluates the duration of API calls to the various S3 API endpoints. Example:
|
|
||||||
|
|
||||||
```
|
|
||||||
api_s3_request_duration_bucket{api_endpoint="CreateMultipartUpload",le="0.5"} 1
|
|
||||||
api_s3_request_duration_sum{api_endpoint="CreateMultipartUpload"} 0.046340762
|
|
||||||
api_s3_request_duration_count{api_endpoint="CreateMultipartUpload"} 1
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `api_k2v_request_counter` (counter), `api_k2v_error_counter` (counter), `api_k2v_error_duration` (histogram)
|
|
||||||
|
|
||||||
Same as for S3, for the K2V API.
|
|
||||||
|
|
||||||
|
|
||||||
### Metrics of the Web endpoint
|
|
||||||
|
|
||||||
|
|
||||||
#### `web_request_counter` (counter)
|
|
||||||
|
|
||||||
Number of requests to the web endpoint
|
|
||||||
|
|
||||||
```
|
|
||||||
web_request_counter{method="GET"} 80
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `web_request_duration` (histogram)
|
|
||||||
|
|
||||||
Duration of requests to the web endpoint
|
|
||||||
|
|
||||||
```
|
|
||||||
web_request_duration_bucket{method="GET",le="0.5"} 80
|
|
||||||
web_request_duration_sum{method="GET"} 1.0528433229999998
|
|
||||||
web_request_duration_count{method="GET"} 80
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `web_error_counter` (counter)
|
|
||||||
|
|
||||||
Number of requests to the web endpoint resulting in errors
|
|
||||||
|
|
||||||
```
|
|
||||||
web_error_counter{method="GET",status_code="404 Not Found"} 64
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Metrics of the data block manager
|
|
||||||
|
|
||||||
#### `block_bytes_read`, `block_bytes_written` (counter)
|
|
||||||
|
|
||||||
Number of bytes read/written to/from disk in the data storage directory.
|
|
||||||
|
|
||||||
```
|
|
||||||
block_bytes_read 120586322022
|
|
||||||
block_bytes_written 3386618077
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `block_read_duration`, `block_write_duration` (histograms)
|
|
||||||
|
|
||||||
Evaluates the duration of the reading/writing of individual data blocks in the data storage directory.
|
|
||||||
|
|
||||||
```
|
|
||||||
block_read_duration_bucket{le="0.5"} 169229
|
|
||||||
block_read_duration_sum 2761.6902550310056
|
|
||||||
block_read_duration_count 169240
|
|
||||||
block_write_duration_bucket{le="0.5"} 3559
|
|
||||||
block_write_duration_sum 195.59170078500006
|
|
||||||
block_write_duration_count 3571
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `block_delete_counter` (counter)
|
|
||||||
|
|
||||||
Counts the number of data blocks that have been deleted from storage.
|
|
||||||
|
|
||||||
```
|
|
||||||
block_delete_counter 122
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `block_resync_counter` (counter), `block_resync_duration` (histogram)
|
|
||||||
|
|
||||||
Counts the number of resync operations the node has executed, and evaluates their duration.
|
|
||||||
|
|
||||||
```
|
|
||||||
block_resync_counter 308897
|
|
||||||
block_resync_duration_bucket{le="0.5"} 308892
|
|
||||||
block_resync_duration_sum 139.64204196100016
|
|
||||||
block_resync_duration_count 308897
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `block_resync_queue_length` (gauge)
|
|
||||||
|
|
||||||
The number of block hashes currently queued for a resync.
|
|
||||||
This is normal to be nonzero for long periods of time.
|
|
||||||
|
|
||||||
```
|
|
||||||
block_resync_queue_length 0
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `block_resync_errored_blocks` (gauge)
|
|
||||||
|
|
||||||
The number of block hashes that we were unable to resync last time we tried.
|
|
||||||
**THIS SHOULD BE ZERO, OR FALL BACK TO ZERO RAPIDLY, IN A HEALTHY CLUSTER.**
|
|
||||||
Persistent nonzero values indicate that some data is likely to be lost.
|
|
||||||
|
|
||||||
```
|
|
||||||
block_resync_errored_blocks 0
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Metrics related to RPCs (remote procedure calls) between nodes
|
|
||||||
|
|
||||||
#### `rpc_netapp_request_counter` (counter)
|
|
||||||
|
|
||||||
Number of RPC requests emitted
|
|
||||||
|
|
||||||
```
|
|
||||||
rpc_request_counter{from="<this node>",rpc_endpoint="garage_block/manager.rs/Rpc",to="<remote node>"} 176
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `rpc_netapp_error_counter` (counter)
|
|
||||||
|
|
||||||
Number of communication errors (errors in the Netapp library, generally due to disconnected nodes)
|
|
||||||
|
|
||||||
```
|
|
||||||
rpc_netapp_error_counter{from="<this node>",rpc_endpoint="garage_block/manager.rs/Rpc",to="<remote node>"} 354
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `rpc_timeout_counter` (counter)
|
|
||||||
|
|
||||||
Number of RPC timeouts, should be close to zero in a healthy cluster.
|
|
||||||
|
|
||||||
```
|
|
||||||
rpc_timeout_counter{from="<this node>",rpc_endpoint="garage_rpc/membership.rs/SystemRpc",to="<remote node>"} 1
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `rpc_duration` (histogram)
|
|
||||||
|
|
||||||
The duration of internal RPC calls between Garage nodes.
|
|
||||||
|
|
||||||
```
|
|
||||||
rpc_duration_bucket{from="<this node>",rpc_endpoint="garage_block/manager.rs/Rpc",to="<remote node>",le="0.5"} 166
|
|
||||||
rpc_duration_sum{from="<this node>",rpc_endpoint="garage_block/manager.rs/Rpc",to="<remote node>"} 35.172253716
|
|
||||||
rpc_duration_count{from="<this node>",rpc_endpoint="garage_block/manager.rs/Rpc",to="<remote node>"} 174
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Metrics of the metadata table manager
|
|
||||||
|
|
||||||
#### `table_gc_todo_queue_length` (gauge)
|
|
||||||
|
|
||||||
Table garbage collector TODO queue length
|
|
||||||
|
|
||||||
```
|
|
||||||
table_gc_todo_queue_length{table_name="block_ref"} 0
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `table_get_request_counter` (counter), `table_get_request_duration` (histogram)
|
|
||||||
|
|
||||||
Number of get/get_range requests internally made on each table, and their duration.
|
|
||||||
|
|
||||||
```
|
|
||||||
table_get_request_counter{table_name="bucket_alias"} 315
|
|
||||||
table_get_request_duration_bucket{table_name="bucket_alias",le="0.5"} 315
|
|
||||||
table_get_request_duration_sum{table_name="bucket_alias"} 0.048509778000000024
|
|
||||||
table_get_request_duration_count{table_name="bucket_alias"} 315
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### `table_put_request_counter` (counter), `table_put_request_duration` (histogram)
|
|
||||||
|
|
||||||
Number of insert/insert_many requests internally made on this table, and their duration
|
|
||||||
|
|
||||||
```
|
|
||||||
table_put_request_counter{table_name="block_ref"} 677
|
|
||||||
table_put_request_duration_bucket{table_name="block_ref",le="0.5"} 677
|
|
||||||
table_put_request_duration_sum{table_name="block_ref"} 61.617528636
|
|
||||||
table_put_request_duration_count{table_name="block_ref"} 677
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `table_internal_delete_counter` (counter)
|
|
||||||
|
|
||||||
Number of value deletions in the tree (due to GC or repartitioning)
|
|
||||||
|
|
||||||
```
|
|
||||||
table_internal_delete_counter{table_name="block_ref"} 2296
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `table_internal_update_counter` (counter)
|
|
||||||
|
|
||||||
Number of value updates where the value actually changes (includes creation of new key and update of existing key)
|
|
||||||
|
|
||||||
```
|
|
||||||
table_internal_update_counter{table_name="block_ref"} 5996
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `table_merkle_updater_todo_queue_length` (gauge)
|
|
||||||
|
|
||||||
Merkle tree updater TODO queue length (should fall to zero rapidly)
|
|
||||||
|
|
||||||
```
|
|
||||||
table_merkle_updater_todo_queue_length{table_name="block_ref"} 0
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `table_sync_items_received`, `table_sync_items_sent` (counters)
|
|
||||||
|
|
||||||
Number of data items sent to/recieved from other nodes during resync procedures
|
|
||||||
|
|
||||||
```
|
|
||||||
table_sync_items_received{from="<remote node>",table_name="bucket_v2"} 3
|
|
||||||
table_sync_items_sent{table_name="block_ref",to="<remote node>"} 2
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,8 @@ We recommend first following the [quick start guide](@/documentation/quick-start
|
||||||
to get familiar with Garage's command line and usage patterns.
|
to get familiar with Garage's command line and usage patterns.
|
||||||
|
|
||||||
|
|
||||||
## Preparing your environment
|
|
||||||
|
|
||||||
### Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
To run a real-world deployment, make sure the following conditions are met:
|
To run a real-world deployment, make sure the following conditions are met:
|
||||||
|
|
||||||
|
@ -22,6 +21,10 @@ To run a real-world deployment, make sure the following conditions are met:
|
||||||
- Each machine has a public IP address which is reachable by other machines.
|
- Each machine has a public IP address which is reachable by other machines.
|
||||||
Running behind a NAT is likely to be possible but hasn't been tested for the latest version (TODO).
|
Running behind a NAT is likely to be possible but hasn't been tested for the latest version (TODO).
|
||||||
|
|
||||||
|
- Ideally, each machine should have a SSD available in addition to the HDD you are dedicating
|
||||||
|
to Garage. This will allow for faster access to metadata and has the potential
|
||||||
|
to significantly reduce Garage's response times.
|
||||||
|
|
||||||
- This guide will assume you are using Docker containers to deploy Garage on each node.
|
- This guide will assume you are using Docker containers to deploy Garage on each node.
|
||||||
Garage can also be run independently, for instance as a [Systemd service](@/documentation/cookbook/systemd.md).
|
Garage can also be run independently, for instance as a [Systemd service](@/documentation/cookbook/systemd.md).
|
||||||
You can also use an orchestrator such as Nomad or Kubernetes to automatically manage
|
You can also use an orchestrator such as Nomad or Kubernetes to automatically manage
|
||||||
|
@ -46,53 +49,17 @@ available in the different locations of your cluster is roughly the same.
|
||||||
For instance, here, the Mercury node could be moved to Brussels; this would allow the cluster
|
For instance, here, the Mercury node could be moved to Brussels; this would allow the cluster
|
||||||
to store 2 TB of data in total.
|
to store 2 TB of data in total.
|
||||||
|
|
||||||
### Best practices
|
|
||||||
|
|
||||||
- If you have fast dedicated networking between all your nodes, and are planing to store
|
|
||||||
very large files, bump the `block_size` configuration parameter to 10 MB
|
|
||||||
(`block_size = 10485760`).
|
|
||||||
|
|
||||||
- Garage stores its files in two locations: it uses a metadata directory to store frequently-accessed
|
|
||||||
small metadata items, and a data directory to store data blocks of uploaded objects.
|
|
||||||
Ideally, the metadata directory would be stored on an SSD (smaller but faster),
|
|
||||||
and the data directory would be stored on an HDD (larger but slower).
|
|
||||||
|
|
||||||
- For the data directory, Garage already does checksumming and integrity verification,
|
|
||||||
so there is no need to use a filesystem such as BTRFS or ZFS that does it.
|
|
||||||
We recommend using XFS for the data partition, as it has the best performance.
|
|
||||||
EXT4 is not recommended as it has more strict limitations on the number of inodes,
|
|
||||||
which might cause issues with Garage when large numbers of objects are stored.
|
|
||||||
|
|
||||||
- If you only have an HDD and no SSD, it's fine to put your metadata alongside the data
|
|
||||||
on the same drive. Having lots of RAM for your kernel to cache the metadata will
|
|
||||||
help a lot with performance. Make sure to use the LMDB database engine,
|
|
||||||
instead of Sled, which suffers from quite bad performance degradation on HDDs.
|
|
||||||
Sled is still the default for legacy reasons, but is not recommended anymore.
|
|
||||||
|
|
||||||
- For the metadata storage, Garage does not do checksumming and integrity
|
|
||||||
verification on its own. If you are afraid of bitrot/data corruption,
|
|
||||||
put your metadata directory on a BTRFS partition. Otherwise, just use regular
|
|
||||||
EXT4 or XFS.
|
|
||||||
|
|
||||||
- Having a single server with several storage drives is currently not very well
|
|
||||||
supported in Garage ([#218](https://git.deuxfleurs.fr/Deuxfleurs/garage/issues/218)).
|
|
||||||
For an easy setup, just put all your drives in a RAID0 or a ZFS RAIDZ array.
|
|
||||||
If you're adventurous, you can try to format each of your disk as
|
|
||||||
a separate XFS partition, and then run one `garage` daemon per disk drive,
|
|
||||||
or use something like [`mergerfs`](https://github.com/trapexit/mergerfs) to merge
|
|
||||||
all your disks in a single union filesystem that spreads load over them.
|
|
||||||
|
|
||||||
## Get a Docker image
|
## Get a Docker image
|
||||||
|
|
||||||
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).
|
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.8.0`) and not the `latest` tag.
|
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.8.0` but it's up to you
|
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/garage/tags?page=1&ordering=last_updated).
|
to check [the most recent versions on the Docker Hub](https://hub.docker.com/r/dxflrs/amd64_garage/tags?page=1&ordering=last_updated).
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
```
|
```
|
||||||
sudo docker pull dxflrs/garage:v0.8.0
|
sudo docker pull dxflrs/amd64_garage:v0.4.0
|
||||||
```
|
```
|
||||||
|
|
||||||
## Deploying and configuring Garage
|
## Deploying and configuring Garage
|
||||||
|
@ -109,12 +76,11 @@ especially you must consider the following folders/files:
|
||||||
this folder will be your main data storage and must be on a large storage (e.g. large HDD)
|
this folder will be your main data storage and must be on a large storage (e.g. large HDD)
|
||||||
|
|
||||||
|
|
||||||
A valid `/etc/garage.toml` for our cluster would look as follows:
|
A valid `/etc/garage/garage.toml` for our cluster would look as follows:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
metadata_dir = "/var/lib/garage/meta"
|
metadata_dir = "/var/lib/garage/meta"
|
||||||
data_dir = "/var/lib/garage/data"
|
data_dir = "/var/lib/garage/data"
|
||||||
db_engine = "lmdb"
|
|
||||||
|
|
||||||
replication_mode = "3"
|
replication_mode = "3"
|
||||||
|
|
||||||
|
@ -124,6 +90,8 @@ rpc_bind_addr = "[::]:3901"
|
||||||
rpc_public_addr = "<this node's public IP>:3901"
|
rpc_public_addr = "<this node's public IP>:3901"
|
||||||
rpc_secret = "<RPC secret>"
|
rpc_secret = "<RPC secret>"
|
||||||
|
|
||||||
|
bootstrap_peers = []
|
||||||
|
|
||||||
[s3_api]
|
[s3_api]
|
||||||
s3_region = "garage"
|
s3_region = "garage"
|
||||||
api_bind_addr = "[::]:3900"
|
api_bind_addr = "[::]:3900"
|
||||||
|
@ -157,28 +125,13 @@ docker run \
|
||||||
-v /etc/garage.toml:/etc/garage.toml \
|
-v /etc/garage.toml:/etc/garage.toml \
|
||||||
-v /var/lib/garage/meta:/var/lib/garage/meta \
|
-v /var/lib/garage/meta:/var/lib/garage/meta \
|
||||||
-v /var/lib/garage/data:/var/lib/garage/data \
|
-v /var/lib/garage/data:/var/lib/garage/data \
|
||||||
dxflrs/garage:v0.8.0
|
lxpz/garage_amd64:v0.4.0
|
||||||
```
|
```
|
||||||
|
|
||||||
It should be restarted automatically at each reboot.
|
It should be restarted automatically at each reboot.
|
||||||
Please note that we use host networking as otherwise Docker containers
|
Please note that we use host networking as otherwise Docker containers
|
||||||
can not communicate with IPv6.
|
can not communicate with IPv6.
|
||||||
|
|
||||||
If you want to use `docker-compose`, you may use the following `docker-compose.yml` file as a reference:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
version: "3"
|
|
||||||
services:
|
|
||||||
garage:
|
|
||||||
image: dxflrs/garage:v0.8.0
|
|
||||||
network_mode: "host"
|
|
||||||
restart: unless-stopped
|
|
||||||
volumes:
|
|
||||||
- /etc/garage.toml:/etc/garage.toml
|
|
||||||
- /var/lib/garage/meta:/var/lib/garage/meta
|
|
||||||
- /var/lib/garage/data:/var/lib/garage/data
|
|
||||||
```
|
|
||||||
|
|
||||||
Upgrading between Garage versions should be supported transparently,
|
Upgrading between Garage versions should be supported transparently,
|
||||||
but please check the relase notes before doing so!
|
but please check the relase notes before doing so!
|
||||||
To upgrade, simply stop and remove this container and
|
To upgrade, simply stop and remove this container and
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
+++
|
+++
|
||||||
title = "Recovering from failures"
|
title = "Recovering from failures"
|
||||||
weight = 50
|
weight = 35
|
||||||
+++
|
+++
|
||||||
|
|
||||||
Garage is meant to work on old, second-hand hardware.
|
Garage is meant to work on old, second-hand hardware.
|
||||||
|
|
|
@ -70,16 +70,14 @@ A possible configuration:
|
||||||
|
|
||||||
```nginx
|
```nginx
|
||||||
upstream s3_backend {
|
upstream s3_backend {
|
||||||
# If you have a garage instance locally.
|
# if you have a garage instance locally
|
||||||
server 127.0.0.1:3900;
|
server 127.0.0.1:3900;
|
||||||
# You can also put your other instances.
|
# you can also put your other instances
|
||||||
server 192.168.1.3:3900;
|
server 192.168.1.3:3900;
|
||||||
# Domain names also work.
|
# domain names also work
|
||||||
server garage1.example.com:3900;
|
server garage1.example.com:3900;
|
||||||
# A "backup" server is only used if all others have failed.
|
# you can assign weights if you have some servers
|
||||||
server garage-remote.example.com:3900 backup;
|
# that are more powerful than others
|
||||||
# You can assign weights if you have some servers
|
|
||||||
# that can serve more requests than others.
|
|
||||||
server garage2.example.com:3900 weight=2;
|
server garage2.example.com:3900 weight=2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,8 +96,6 @@ server {
|
||||||
proxy_pass http://s3_backend;
|
proxy_pass http://s3_backend;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
# Disable buffering to a temporary file.
|
|
||||||
proxy_max_temp_file_size 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -284,25 +280,3 @@ Traefik's caching middleware is only available on [entreprise version](https://d
|
||||||
[http.middlewares]
|
[http.middlewares]
|
||||||
[http.middlewares.gzip_compress.compress]
|
[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.
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
+++
|
+++
|
||||||
title = "Upgrading Garage"
|
title = "Upgrading Garage"
|
||||||
weight = 60
|
weight = 40
|
||||||
+++
|
+++
|
||||||
|
|
||||||
Garage is a stateful clustered application, where all nodes are communicating together and share data structures.
|
Garage is a stateful clustered application, where all nodes are communicating together and share data structures.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
+++
|
+++
|
||||||
title = "Design"
|
title = "Design"
|
||||||
weight = 6
|
weight = 5
|
||||||
sort_by = "weight"
|
sort_by = "weight"
|
||||||
template = "documentation.html"
|
template = "documentation.html"
|
||||||
+++
|
+++
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
+++
|
+++
|
||||||
title = "Benchmarks"
|
title = "Benchmarks"
|
||||||
weight = 40
|
weight = 10
|
||||||
+++
|
+++
|
||||||
|
|
||||||
With Garage, we wanted to build a software defined storage service that follow the [KISS principle](https://en.wikipedia.org/wiki/KISS_principle),
|
With Garage, we wanted to build a software defined storage service that follow the [KISS principle](https://en.wikipedia.org/wiki/KISS_principle),
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
+++
|
+++
|
||||||
title = "Goals and use cases"
|
title = "Goals and use cases"
|
||||||
weight = 10
|
weight = 5
|
||||||
+++
|
+++
|
||||||
|
|
||||||
## Goals and non-goals
|
## Goals and non-goals
|
||||||
|
|
||||||
Garage is a lightweight geo-distributed data store that implements the
|
Garage is a lightweight geo-distributed data store that implements the
|
||||||
[Amazon S3](https://docs.aws.amazon.com/AmazonS3/latest/API/Welcome.html)
|
[Amazon S3](https://docs.aws.amazon.com/AmazonS3/latest/API/Welcome.html)
|
||||||
object storage protocol. It enables applications to store large blobs such
|
object storage protocole. It enables applications to store large blobs such
|
||||||
as pictures, video, images, documents, etc., in a redundant multi-node
|
as pictures, video, images, documents, etc., in a redundant multi-node
|
||||||
setting. S3 is versatile enough to also be used to publish a static
|
setting. S3 is versatile enough to also be used to publish a static
|
||||||
website.
|
website.
|
||||||
|
|
||||||
Garage is an opinionated object storage solution, we focus on the following **desirable properties**:
|
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).
|
- **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.
|
- **Highly resilient**: highly resilient to network failures, network latency, disk failures, sysadmin failures.
|
||||||
- **Simple**: simple to understand, simple to operate, simple to debug.
|
- **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.
|
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):
|
The following has been identified as **non-goals** (if these points matter to you, you should not use Garage):
|
||||||
|
|
|
@ -20,49 +20,6 @@ In the meantime, you can find some information at the following links:
|
||||||
- [an old design draft](@/documentation/working-documents/design-draft.md)
|
- [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
|
## Garbage collection
|
||||||
|
|
||||||
A faulty garbage collection procedure has been the cause of
|
A faulty garbage collection procedure has been the cause of
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
+++
|
+++
|
||||||
title = "Related work"
|
title = "Related work"
|
||||||
weight = 50
|
weight = 15
|
||||||
+++
|
+++
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
+++
|
+++
|
||||||
title = "Development"
|
title = "Development"
|
||||||
weight = 7
|
weight = 6
|
||||||
sort_by = "weight"
|
sort_by = "weight"
|
||||||
template = "documentation.html"
|
template = "documentation.html"
|
||||||
+++
|
+++
|
||||||
|
|
|
@ -9,15 +9,6 @@ Let's start your Garage journey!
|
||||||
In this chapter, we explain how to deploy Garage as a single-node server
|
In this chapter, we explain how to deploy Garage as a single-node server
|
||||||
and how to interact with it.
|
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.
|
Our goal is to introduce you to Garage's workflows.
|
||||||
Following this guide is recommended before moving on to
|
Following this guide is recommended before moving on to
|
||||||
[configuring a multi-node cluster](@/documentation/cookbook/real-world.md).
|
[configuring a multi-node cluster](@/documentation/cookbook/real-world.md).
|
||||||
|
@ -42,25 +33,25 @@ you can [build Garage from source](@/documentation/cookbook/from-source.md).
|
||||||
|
|
||||||
## Configuring and starting Garage
|
## Configuring and starting Garage
|
||||||
|
|
||||||
### Generating a first configuration file
|
### Writing a first configuration file
|
||||||
|
|
||||||
This first configuration file should allow you to get started easily with the simplest
|
This first configuration file should allow you to get started easily with the simplest
|
||||||
possible Garage deployment.
|
possible Garage deployment.
|
||||||
|
**Save it as `/etc/garage.toml`.**
|
||||||
|
You can also store it somewhere else, but you will have to specify `-c path/to/garage.toml`
|
||||||
|
at each invocation of the `garage` binary (for example: `garage -c ./garage.toml server`, `garage -c ./garage.toml status`).
|
||||||
|
|
||||||
We will create it with the following command line
|
```toml
|
||||||
to generate unique and private secrets for security reasons:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cat > garage.toml <<EOF
|
|
||||||
metadata_dir = "/tmp/meta"
|
metadata_dir = "/tmp/meta"
|
||||||
data_dir = "/tmp/data"
|
data_dir = "/tmp/data"
|
||||||
db_engine = "lmdb"
|
|
||||||
|
|
||||||
replication_mode = "none"
|
replication_mode = "none"
|
||||||
|
|
||||||
rpc_bind_addr = "[::]:3901"
|
rpc_bind_addr = "[::]:3901"
|
||||||
rpc_public_addr = "127.0.0.1:3901"
|
rpc_public_addr = "127.0.0.1:3901"
|
||||||
rpc_secret = "$(openssl rand -hex 32)"
|
rpc_secret = "1799bccfd7411eddcf9ebd316bc1f5287ad12a68094e1c6ac6abde7e6feae1ec"
|
||||||
|
|
||||||
|
bootstrap_peers = []
|
||||||
|
|
||||||
[s3_api]
|
[s3_api]
|
||||||
s3_region = "garage"
|
s3_region = "garage"
|
||||||
|
@ -71,26 +62,12 @@ root_domain = ".s3.garage.localhost"
|
||||||
bind_addr = "[::]:3902"
|
bind_addr = "[::]:3902"
|
||||||
root_domain = ".web.garage.localhost"
|
root_domain = ".web.garage.localhost"
|
||||||
index = "index.html"
|
index = "index.html"
|
||||||
|
|
||||||
[k2v_api]
|
|
||||||
api_bind_addr = "[::]:3904"
|
|
||||||
|
|
||||||
[admin]
|
|
||||||
api_bind_addr = "0.0.0.0:3903"
|
|
||||||
admin_token = "$(openssl rand -base64 32)"
|
|
||||||
EOF
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Now that your configuration file has been created, you can put
|
The `rpc_secret` value provided above is just an example. It will work, but in
|
||||||
it in the right place. By default, garage looks at **`/etc/garage.toml`.**
|
order to secure your cluster you will need to use another one. You can generate
|
||||||
|
such a value with `openssl rand -hex 32`.
|
||||||
|
|
||||||
You can also store it somewhere else, but you will have to specify `-c path/to/garage.toml`
|
|
||||||
at each invocation of the `garage` binary (for example: `garage -c ./garage.toml server`, `garage -c ./garage.toml status`).
|
|
||||||
|
|
||||||
As you can see, the `rpc_secret` is a 32 bytes hexadecimal string.
|
|
||||||
You can regenerate it with `openssl rand -hex 32`.
|
|
||||||
If you target a cluster deployment with multiple nodes, make sure that
|
|
||||||
you use the same value for all nodes.
|
|
||||||
|
|
||||||
As you can see in the `metadata_dir` and `data_dir` parameters, we are saving Garage's data
|
As you can see in the `metadata_dir` and `data_dir` parameters, we are saving Garage's data
|
||||||
in `/tmp` which gets erased when your system reboots. This means that data stored on this
|
in `/tmp` which gets erased when your system reboots. This means that data stored on this
|
||||||
|
@ -233,7 +210,6 @@ Now that we have a bucket and a key, we need to give permissions to the key on t
|
||||||
garage bucket allow \
|
garage bucket allow \
|
||||||
--read \
|
--read \
|
||||||
--write \
|
--write \
|
||||||
--owner \
|
|
||||||
nextcloud-bucket \
|
nextcloud-bucket \
|
||||||
--key nextcloud-app-key
|
--key nextcloud-app-key
|
||||||
```
|
```
|
||||||
|
@ -247,73 +223,54 @@ garage bucket info nextcloud-bucket
|
||||||
|
|
||||||
## Uploading and downlading from Garage
|
## Uploading and downlading from Garage
|
||||||
|
|
||||||
To download and upload files on garage, we can use a third-party tool named `awscli`.
|
We recommend the use of MinIO Client to interact with Garage files (`mc`).
|
||||||
|
Instructions to install it and use it are provided on the
|
||||||
|
[MinIO website](https://docs.min.io/docs/minio-client-quickstart-guide.html).
|
||||||
|
Before reading the following, you need a working `mc` command on your path.
|
||||||
|
|
||||||
|
Note that on certain Linux distributions such as Arch Linux, the Minio client binary
|
||||||
|
is called `mcli` instead of `mc` (to avoid name clashes with the Midnight Commander).
|
||||||
|
|
||||||
### Install and configure `awscli`
|
### Configure `mc`
|
||||||
|
|
||||||
If you have python on your system, you can install it with:
|
You need your access key and secret key created above.
|
||||||
|
We will assume you are invoking `mc` on the same machine as the Garage server,
|
||||||
|
your S3 API endpoint is therefore `http://127.0.0.1:3900`.
|
||||||
|
For this whole configuration, you must set an alias name: we chose `my-garage`, that you will used for all commands.
|
||||||
|
|
||||||
|
Adapt the following command accordingly and run it:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python -m pip install --user awscli
|
mc alias set \
|
||||||
|
my-garage \
|
||||||
|
http://127.0.0.1:3900 \
|
||||||
|
<access key> \
|
||||||
|
<secret key> \
|
||||||
|
--api S3v4
|
||||||
```
|
```
|
||||||
|
|
||||||
Now that `awscli` is installed, you must configure it to talk to your Garage instance,
|
### Use `mc`
|
||||||
with your key. There are multiple ways to do that, the simplest one is to create a file
|
|
||||||
named `~/.awsrc` with this content:
|
You can not list buckets from `mc` currently.
|
||||||
|
|
||||||
|
But the following commands and many more should work:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export AWS_ACCESS_KEY_ID=xxxx # put your Key ID here
|
mc cp image.png my-garage/nextcloud-bucket
|
||||||
export AWS_SECRET_ACCESS_KEY=xxxx # put your Secret key here
|
mc cp my-garage/nextcloud-bucket/image.png .
|
||||||
export AWS_DEFAULT_REGION='garage'
|
mc ls my-garage/nextcloud-bucket
|
||||||
export AWS_ENDPOINT='http://localhost:3900'
|
mc mirror localdir/ my-garage/another-bucket
|
||||||
|
|
||||||
function aws { command aws --endpoint-url $AWS_ENDPOINT $@ ; }
|
|
||||||
aws --version
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Now, each time you want to use `awscli` on this target, run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
source ~/.awsrc
|
|
||||||
```
|
|
||||||
|
|
||||||
*You can create multiple files with different names if you
|
|
||||||
have multiple Garage clusters or different keys.
|
|
||||||
Switching from one cluster to another is as simple as
|
|
||||||
sourcing the right file.*
|
|
||||||
|
|
||||||
### Example usage of `awscli`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# list buckets
|
|
||||||
aws s3 ls
|
|
||||||
|
|
||||||
# list objects of a bucket
|
|
||||||
aws s3 ls s3://my_files
|
|
||||||
|
|
||||||
# copy from your filesystem to garage
|
|
||||||
aws s3 cp /proc/cpuinfo s3://my_files/cpuinfo.txt
|
|
||||||
|
|
||||||
# copy from garage to your filesystem
|
|
||||||
aws s3 cp s3/my_files/cpuinfo.txt /tmp/cpuinfo.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that you can use `awscli` for more advanced operations like
|
|
||||||
creating a bucket, pre-signing a request or managing your website.
|
|
||||||
[Read the full documentation to know more](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3/index.html).
|
|
||||||
|
|
||||||
Some features are however not implemented like ACL or policy.
|
|
||||||
Check [our s3 compatibility list](@/documentation/reference-manual/s3-compatibility.md).
|
|
||||||
|
|
||||||
### Other tools for interacting with Garage
|
### Other tools for interacting with Garage
|
||||||
|
|
||||||
The following tools can also be used to send and recieve files from/to Garage:
|
The following tools can also be used to send and recieve files from/to Garage:
|
||||||
|
|
||||||
- [minio-client](@/documentation/connect/cli.md#minio-client)
|
- the [AWS CLI](https://aws.amazon.com/cli/)
|
||||||
- [s3cmd](@/documentation/connect/cli.md#s3cmd)
|
- [`rclone`](https://rclone.org/)
|
||||||
- [rclone](@/documentation/connect/cli.md#rclone)
|
- [Cyberduck](https://cyberduck.io/)
|
||||||
- [Cyberduck](@/documentation/connect/cli.md#cyberduck)
|
- [`s3cmd`](https://s3tools.org/s3cmd)
|
||||||
- [WinSCP](@/documentation/connect/cli.md#winscp)
|
|
||||||
|
|
||||||
An exhaustive list is maintained in the ["Integrations" > "Browsing tools" section](@/documentation/connect/_index.md).
|
Refer to the ["Integrations" section](@/documentation/connect/_index.md) to learn how to
|
||||||
|
configure application and command line utilities to integrate with Garage.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
+++
|
+++
|
||||||
title = "Reference Manual"
|
title = "Reference Manual"
|
||||||
weight = 5
|
weight = 4
|
||||||
sort_by = "weight"
|
sort_by = "weight"
|
||||||
template = "documentation.html"
|
template = "documentation.html"
|
||||||
+++
|
+++
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
+++
|
+++
|
||||||
title = "Administration API"
|
title = "Administration API"
|
||||||
weight = 60
|
weight = 16
|
||||||
+++
|
+++
|
||||||
|
|
||||||
The Garage administration API is accessible through a dedicated server whose
|
The Garage administration API is accessible through a dedicated server whose
|
||||||
|
@ -47,13 +47,598 @@ Returns internal Garage metrics in Prometheus format.
|
||||||
|
|
||||||
### Cluster operations
|
### Cluster operations
|
||||||
|
|
||||||
These endpoints are defined on a dedicated [Redocly page](https://garagehq.deuxfleurs.fr/api/garage-admin-v0.html). You can also download its [OpenAPI specification](https://garagehq.deuxfleurs.fr/api/garage-admin-v0.yml).
|
#### GetClusterStatus `GET /v0/status`
|
||||||
|
|
||||||
Requesting the API from the command line can be as simple as running:
|
Returns the cluster's current status in JSON, including:
|
||||||
|
|
||||||
```bash
|
- ID of the node being queried and its version of the Garage daemon
|
||||||
curl -H 'Authorization: Bearer s3cr3t' http://localhost:3903/v0/status | jq
|
- Live nodes
|
||||||
|
- Currently configured cluster layout
|
||||||
|
- Staged changes to the cluster layout
|
||||||
|
|
||||||
|
Example response body:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"node": "ec79480e0ce52ae26fd00c9da684e4fa56658d9c64cdcecb094e936de0bfe71f",
|
||||||
|
"garage_version": "git:v0.8.0",
|
||||||
|
"knownNodes": {
|
||||||
|
"ec79480e0ce52ae26fd00c9da684e4fa56658d9c64cdcecb094e936de0bfe71f": {
|
||||||
|
"addr": "10.0.0.11:3901",
|
||||||
|
"is_up": true,
|
||||||
|
"last_seen_secs_ago": 9,
|
||||||
|
"hostname": "node1"
|
||||||
|
},
|
||||||
|
"4a6ae5a1d0d33bf895f5bb4f0a418b7dc94c47c0dd2eb108d1158f3c8f60b0ff": {
|
||||||
|
"addr": "10.0.0.12:3901",
|
||||||
|
"is_up": true,
|
||||||
|
"last_seen_secs_ago": 1,
|
||||||
|
"hostname": "node2"
|
||||||
|
},
|
||||||
|
"23ffd0cdd375ebff573b20cc5cef38996b51c1a7d6dbcf2c6e619876e507cf27": {
|
||||||
|
"addr": "10.0.0.21:3901",
|
||||||
|
"is_up": true,
|
||||||
|
"last_seen_secs_ago": 7,
|
||||||
|
"hostname": "node3"
|
||||||
|
},
|
||||||
|
"e2ee7984ee65b260682086ec70026165903c86e601a4a5a501c1900afe28d84b": {
|
||||||
|
"addr": "10.0.0.22:3901",
|
||||||
|
"is_up": true,
|
||||||
|
"last_seen_secs_ago": 1,
|
||||||
|
"hostname": "node4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"version": 12,
|
||||||
|
"roles": {
|
||||||
|
"ec79480e0ce52ae26fd00c9da684e4fa56658d9c64cdcecb094e936de0bfe71f": {
|
||||||
|
"zone": "dc1",
|
||||||
|
"capacity": 4,
|
||||||
|
"tags": [
|
||||||
|
"node1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"4a6ae5a1d0d33bf895f5bb4f0a418b7dc94c47c0dd2eb108d1158f3c8f60b0ff": {
|
||||||
|
"zone": "dc1",
|
||||||
|
"capacity": 6,
|
||||||
|
"tags": [
|
||||||
|
"node2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"23ffd0cdd375ebff573b20cc5cef38996b51c1a7d6dbcf2c6e619876e507cf27": {
|
||||||
|
"zone": "dc2",
|
||||||
|
"capacity": 10,
|
||||||
|
"tags": [
|
||||||
|
"node3"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"stagedRoleChanges": {
|
||||||
|
"e2ee7984ee65b260682086ec70026165903c86e601a4a5a501c1900afe28d84b": {
|
||||||
|
"zone": "dc2",
|
||||||
|
"capacity": 5,
|
||||||
|
"tags": [
|
||||||
|
"node4"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
For more advanced use cases, we recommend using a SDK.
|
#### ConnectClusterNodes `POST /v0/connect`
|
||||||
[Go to the "Build your own app" section to know how to use our SDKs](@/documentation/build/_index.md)
|
|
||||||
|
Instructs this Garage node to connect to other Garage nodes at specified addresses.
|
||||||
|
|
||||||
|
Example request body:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
"ec79480e0ce52ae26fd00c9da684e4fa56658d9c64cdcecb094e936de0bfe71f@10.0.0.11:3901",
|
||||||
|
"4a6ae5a1d0d33bf895f5bb4f0a418b7dc94c47c0dd2eb108d1158f3c8f60b0ff@10.0.0.12:3901"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
The format of the string for a node to connect to is: `<node ID>@<ip address>:<port>`, same as in the `garage node connect` CLI call.
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"error": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"error": "Handshake error"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### GetClusterLayout `GET /v0/layout`
|
||||||
|
|
||||||
|
Returns the cluster's current layout in JSON, including:
|
||||||
|
|
||||||
|
- Currently configured cluster layout
|
||||||
|
- Staged changes to the cluster layout
|
||||||
|
|
||||||
|
(the info returned by this endpoint is a subset of the info returned by GetClusterStatus)
|
||||||
|
|
||||||
|
Example response body:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": 12,
|
||||||
|
"roles": {
|
||||||
|
"ec79480e0ce52ae26fd00c9da684e4fa56658d9c64cdcecb094e936de0bfe71f": {
|
||||||
|
"zone": "dc1",
|
||||||
|
"capacity": 4,
|
||||||
|
"tags": [
|
||||||
|
"node1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"4a6ae5a1d0d33bf895f5bb4f0a418b7dc94c47c0dd2eb108d1158f3c8f60b0ff": {
|
||||||
|
"zone": "dc1",
|
||||||
|
"capacity": 6,
|
||||||
|
"tags": [
|
||||||
|
"node2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"23ffd0cdd375ebff573b20cc5cef38996b51c1a7d6dbcf2c6e619876e507cf27": {
|
||||||
|
"zone": "dc2",
|
||||||
|
"capacity": 10,
|
||||||
|
"tags": [
|
||||||
|
"node3"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"stagedRoleChanges": {
|
||||||
|
"e2ee7984ee65b260682086ec70026165903c86e601a4a5a501c1900afe28d84b": {
|
||||||
|
"zone": "dc2",
|
||||||
|
"capacity": 5,
|
||||||
|
"tags": [
|
||||||
|
"node4"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### UpdateClusterLayout `POST /v0/layout`
|
||||||
|
|
||||||
|
Send modifications to the cluster layout. These modifications will
|
||||||
|
be included in the staged role changes, visible in subsequent calls
|
||||||
|
of `GetClusterLayout`. Once the set of staged changes is satisfactory,
|
||||||
|
the user may call `ApplyClusterLayout` to apply the changed changes,
|
||||||
|
or `Revert ClusterLayout` to clear all of the staged changes in
|
||||||
|
the layout.
|
||||||
|
|
||||||
|
Request body format:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
<node_id>: {
|
||||||
|
"capacity": <new_capacity>,
|
||||||
|
"zone": <new_zone>,
|
||||||
|
"tags": [
|
||||||
|
<new_tag>,
|
||||||
|
...
|
||||||
|
]
|
||||||
|
},
|
||||||
|
<node_id_to_remove>: null,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Contrary to the CLI that may update only a subset of the fields
|
||||||
|
`capacity`, `zone` and `tags`, when calling this API all of these
|
||||||
|
values must be specified.
|
||||||
|
|
||||||
|
|
||||||
|
#### ApplyClusterLayout `POST /v0/layout/apply`
|
||||||
|
|
||||||
|
Applies to the cluster the layout changes currently registered as
|
||||||
|
staged layout changes.
|
||||||
|
|
||||||
|
Request body format:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": 13
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Similarly to the CLI, the body must include the version of the new layout
|
||||||
|
that will be created, which MUST be 1 + the value of the currently
|
||||||
|
existing layout in the cluster.
|
||||||
|
|
||||||
|
#### RevertClusterLayout `POST /v0/layout/revert`
|
||||||
|
|
||||||
|
Clears all of the staged layout changes.
|
||||||
|
|
||||||
|
Request body format:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": 13
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Reverting the staged changes is done by incrementing the version number
|
||||||
|
and clearing the contents of the staged change list.
|
||||||
|
Similarly to the CLI, the body must include the incremented
|
||||||
|
version number, which MUST be 1 + the value of the currently
|
||||||
|
existing layout in the cluster.
|
||||||
|
|
||||||
|
|
||||||
|
### Access key operations
|
||||||
|
|
||||||
|
#### ListKeys `GET /v0/key`
|
||||||
|
|
||||||
|
Returns all API access keys in the cluster.
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "GK31c2f218a2e44f485b94239e",
|
||||||
|
"name": "test"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "GKe10061ac9c2921f09e4c5540",
|
||||||
|
"name": "test2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### CreateKey `POST /v0/key`
|
||||||
|
|
||||||
|
Creates a new API access key.
|
||||||
|
|
||||||
|
Request body format:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "NameOfMyKey"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### ImportKey `POST /v0/key/import`
|
||||||
|
|
||||||
|
Imports an existing API key.
|
||||||
|
|
||||||
|
Request body format:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"accessKeyId": "GK31c2f218a2e44f485b94239e",
|
||||||
|
"secretAccessKey": "b892c0665f0ada8a4755dae98baa3b133590e11dae3bcc1f9d769d67f16c3835",
|
||||||
|
"name": "NameOfMyKey"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### GetKeyInfo `GET /v0/key?id=<acces key id>`
|
||||||
|
#### GetKeyInfo `GET /v0/key?search=<pattern>`
|
||||||
|
|
||||||
|
Returns information about the requested API access key.
|
||||||
|
|
||||||
|
If `id` is set, the key is looked up using its exact identifier (faster).
|
||||||
|
If `search` is set, the key is looked up using its name or prefix
|
||||||
|
of identifier (slower, all keys are enumerated to do this).
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "test",
|
||||||
|
"accessKeyId": "GK31c2f218a2e44f485b94239e",
|
||||||
|
"secretAccessKey": "b892c0665f0ada8a4755dae98baa3b133590e11dae3bcc1f9d769d67f16c3835",
|
||||||
|
"permissions": {
|
||||||
|
"createBucket": false
|
||||||
|
},
|
||||||
|
"buckets": [
|
||||||
|
{
|
||||||
|
"id": "70dc3bed7fe83a75e46b66e7ddef7d56e65f3c02f9f80b6749fb97eccb5e1033",
|
||||||
|
"globalAliases": [
|
||||||
|
"test2"
|
||||||
|
],
|
||||||
|
"localAliases": [],
|
||||||
|
"permissions": {
|
||||||
|
"read": true,
|
||||||
|
"write": true,
|
||||||
|
"owner": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "d7452a935e663fc1914f3a5515163a6d3724010ce8dfd9e4743ca8be5974f995",
|
||||||
|
"globalAliases": [
|
||||||
|
"test3"
|
||||||
|
],
|
||||||
|
"localAliases": [],
|
||||||
|
"permissions": {
|
||||||
|
"read": true,
|
||||||
|
"write": true,
|
||||||
|
"owner": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e6a14cd6a27f48684579ec6b381c078ab11697e6bc8513b72b2f5307e25fff9b",
|
||||||
|
"globalAliases": [],
|
||||||
|
"localAliases": [
|
||||||
|
"test"
|
||||||
|
],
|
||||||
|
"permissions": {
|
||||||
|
"read": true,
|
||||||
|
"write": true,
|
||||||
|
"owner": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "96470e0df00ec28807138daf01915cfda2bee8eccc91dea9558c0b4855b5bf95",
|
||||||
|
"globalAliases": [
|
||||||
|
"alex"
|
||||||
|
],
|
||||||
|
"localAliases": [],
|
||||||
|
"permissions": {
|
||||||
|
"read": true,
|
||||||
|
"write": true,
|
||||||
|
"owner": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### DeleteKey `DELETE /v0/key?id=<acces key id>`
|
||||||
|
|
||||||
|
Deletes an API access key.
|
||||||
|
|
||||||
|
#### UpdateKey `POST /v0/key?id=<acces key id>`
|
||||||
|
|
||||||
|
Updates information about the specified API access key.
|
||||||
|
|
||||||
|
Request body format:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "NameOfMyKey",
|
||||||
|
"allow": {
|
||||||
|
"createBucket": true,
|
||||||
|
},
|
||||||
|
"deny": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
All fields (`name`, `allow` and `deny`) are optionnal.
|
||||||
|
If they are present, the corresponding modifications are applied to the key, otherwise nothing is changed.
|
||||||
|
The possible flags in `allow` and `deny` are: `createBucket`.
|
||||||
|
|
||||||
|
|
||||||
|
### Bucket operations
|
||||||
|
|
||||||
|
#### ListBuckets `GET /v0/bucket`
|
||||||
|
|
||||||
|
Returns all storage buckets in the cluster.
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "70dc3bed7fe83a75e46b66e7ddef7d56e65f3c02f9f80b6749fb97eccb5e1033",
|
||||||
|
"globalAliases": [
|
||||||
|
"test2"
|
||||||
|
],
|
||||||
|
"localAliases": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "96470e0df00ec28807138daf01915cfda2bee8eccc91dea9558c0b4855b5bf95",
|
||||||
|
"globalAliases": [
|
||||||
|
"alex"
|
||||||
|
],
|
||||||
|
"localAliases": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "d7452a935e663fc1914f3a5515163a6d3724010ce8dfd9e4743ca8be5974f995",
|
||||||
|
"globalAliases": [
|
||||||
|
"test3"
|
||||||
|
],
|
||||||
|
"localAliases": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e6a14cd6a27f48684579ec6b381c078ab11697e6bc8513b72b2f5307e25fff9b",
|
||||||
|
"globalAliases": [],
|
||||||
|
"localAliases": [
|
||||||
|
{
|
||||||
|
"accessKeyId": "GK31c2f218a2e44f485b94239e",
|
||||||
|
"alias": "test"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### GetBucketInfo `GET /v0/bucket?id=<bucket id>`
|
||||||
|
#### GetBucketInfo `GET /v0/bucket?globalAlias=<alias>`
|
||||||
|
|
||||||
|
Returns information about the requested storage bucket.
|
||||||
|
|
||||||
|
If `id` is set, the bucket is looked up using its exact identifier.
|
||||||
|
If `globalAlias` is set, the bucket is looked up using its global alias.
|
||||||
|
(both are fast)
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### CreateBucket `POST /v0/bucket`
|
||||||
|
|
||||||
|
Creates a new storage bucket.
|
||||||
|
|
||||||
|
Request body format:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"globalAlias": "NameOfMyBucket"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
OR
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"localAlias": {
|
||||||
|
"accessKeyId": "GK31c2f218a2e44f485b94239e",
|
||||||
|
"alias": "NameOfMyBucket",
|
||||||
|
"allow": {
|
||||||
|
"read": true,
|
||||||
|
"write": true,
|
||||||
|
"owner": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
OR
|
||||||
|
|
||||||
|
```json
|
||||||
|
{}
|
||||||
|
```
|
||||||
|
|
||||||
|
Creates a new bucket, either with a global alias, a local one,
|
||||||
|
or no alias at all.
|
||||||
|
|
||||||
|
Technically, you can also specify both `globalAlias` and `localAlias` and that would create
|
||||||
|
two aliases, but I don't see why you would want to do that.
|
||||||
|
|
||||||
|
#### DeleteBucket `DELETE /v0/bucket?id=<bucket id>`
|
||||||
|
|
||||||
|
Deletes a storage bucket. A bucket cannot be deleted if it is not empty.
|
||||||
|
|
||||||
|
Warning: this will delete all aliases associated with the bucket!
|
||||||
|
|
||||||
|
#### UpdateBucket `PUT /v0/bucket?id=<bucket id>`
|
||||||
|
|
||||||
|
Updates configuration of the given bucket.
|
||||||
|
|
||||||
|
Request body format:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"websiteAccess": {
|
||||||
|
"enabled": true,
|
||||||
|
"indexDocument": "index.html",
|
||||||
|
"errorDocument": "404.html"
|
||||||
|
},
|
||||||
|
"quotas": {
|
||||||
|
"maxSize": 19029801,
|
||||||
|
"maxObjects": null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
#### BucketAllowKey `POST /v0/bucket/allow`
|
||||||
|
|
||||||
|
Allows a key to do read/write/owner operations on a bucket.
|
||||||
|
|
||||||
|
Request body format:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"bucketId": "e6a14cd6a27f48684579ec6b381c078ab11697e6bc8513b72b2f5307e25fff9b",
|
||||||
|
"accessKeyId": "GK31c2f218a2e44f485b94239e",
|
||||||
|
"permissions": {
|
||||||
|
"read": true,
|
||||||
|
"write": true,
|
||||||
|
"owner": true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Flags in `permissions` which have the value `true` will be activated.
|
||||||
|
Other flags will remain unchanged.
|
||||||
|
|
||||||
|
#### BucketDenyKey `POST /v0/bucket/deny`
|
||||||
|
|
||||||
|
Denies a key from doing read/write/owner operations on a bucket.
|
||||||
|
|
||||||
|
Request body format:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"bucketId": "e6a14cd6a27f48684579ec6b381c078ab11697e6bc8513b72b2f5307e25fff9b",
|
||||||
|
"accessKeyId": "GK31c2f218a2e44f485b94239e",
|
||||||
|
"permissions": {
|
||||||
|
"read": false,
|
||||||
|
"write": false,
|
||||||
|
"owner": true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Flags in `permissions` which have the value `true` will be deactivated.
|
||||||
|
Other flags will remain unchanged.
|
||||||
|
|
||||||
|
|
||||||
|
### Operations on bucket aliases
|
||||||
|
|
||||||
|
#### GlobalAliasBucket `PUT /v0/bucket/alias/global?id=<bucket id>&alias=<global alias>`
|
||||||
|
|
||||||
|
Empty body. Creates a global alias for a bucket.
|
||||||
|
|
||||||
|
#### GlobalUnaliasBucket `DELETE /v0/bucket/alias/global?id=<bucket id>&alias=<global alias>`
|
||||||
|
|
||||||
|
Removes a global alias for a bucket.
|
||||||
|
|
||||||
|
#### LocalAliasBucket `PUT /v0/bucket/alias/local?id=<bucket id>&accessKeyId=<access key ID>&alias=<local alias>`
|
||||||
|
|
||||||
|
Empty body. Creates a local alias for a bucket in the namespace of a specific access key.
|
||||||
|
|
||||||
|
#### LocalUnaliasBucket `DELETE /v0/bucket/alias/local?id=<bucket id>&accessKeyId<access key ID>&alias=<local alias>`
|
||||||
|
|
||||||
|
Removes a local alias for a bucket in the namespace of a specific access key.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
+++
|
+++
|
||||||
title = "Garage CLI"
|
title = "Garage CLI"
|
||||||
weight = 30
|
weight = 15
|
||||||
+++
|
+++
|
||||||
|
|
||||||
The Garage CLI is mostly self-documented. Make use of the `help` subcommand
|
The Garage CLI is mostly self-documented. Make use of the `help` subcommand
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
+++
|
+++
|
||||||
title = "Configuration file format"
|
title = "Configuration file format"
|
||||||
weight = 20
|
weight = 5
|
||||||
+++
|
+++
|
||||||
|
|
||||||
Here is an example `garage.toml` configuration file that illustrates all of the possible options:
|
Here is an example `garage.toml` configuration file that illustrates all of the possible options:
|
||||||
|
@ -9,12 +9,8 @@ Here is an example `garage.toml` configuration file that illustrates all of the
|
||||||
metadata_dir = "/var/lib/garage/meta"
|
metadata_dir = "/var/lib/garage/meta"
|
||||||
data_dir = "/var/lib/garage/data"
|
data_dir = "/var/lib/garage/data"
|
||||||
|
|
||||||
db_engine = "lmdb"
|
|
||||||
|
|
||||||
block_size = 1048576
|
block_size = 1048576
|
||||||
|
block_manager_background_tranquility = 2
|
||||||
sled_cache_capacity = 134217728
|
|
||||||
sled_flush_every_ms = 2000
|
|
||||||
|
|
||||||
replication_mode = "3"
|
replication_mode = "3"
|
||||||
|
|
||||||
|
@ -31,20 +27,15 @@ bootstrap_peers = [
|
||||||
"212fd62eeaca72c122b45a7f4fa0f55e012aa5e24ac384a72a3016413fa724ff@[fc00:F::1]:3901",
|
"212fd62eeaca72c122b45a7f4fa0f55e012aa5e24ac384a72a3016413fa724ff@[fc00:F::1]:3901",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
consul_host = "consul.service"
|
||||||
|
consul_service_name = "garage-daemon"
|
||||||
|
|
||||||
[consul_discovery]
|
kubernetes_namespace = "garage"
|
||||||
consul_http_addr = "http://127.0.0.1:8500"
|
kubernetes_service_name = "garage-daemon"
|
||||||
service_name = "garage-daemon"
|
kubernetes_skip_crd = false
|
||||||
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]
|
[s3_api]
|
||||||
api_bind_addr = "[::]:3900"
|
api_bind_addr = "[::]:3900"
|
||||||
|
@ -81,47 +72,6 @@ This folder can be placed on an HDD. The space available for `data_dir`
|
||||||
should be counted to determine a node's capacity
|
should be counted to determine a node's capacity
|
||||||
when [adding it to the cluster layout](@/documentation/cookbook/real-world.md).
|
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`
|
### `block_size`
|
||||||
|
|
||||||
Garage splits stored objects in consecutive chunks of size `block_size`
|
Garage splits stored objects in consecutive chunks of size `block_size`
|
||||||
|
@ -137,20 +87,16 @@ files will remain available. This however means that chunks from existing files
|
||||||
will not be deduplicated with chunks from newly uploaded files, meaning you
|
will not be deduplicated with chunks from newly uploaded files, meaning you
|
||||||
might use more storage space that is optimally possible.
|
might use more storage space that is optimally possible.
|
||||||
|
|
||||||
### `sled_cache_capacity`
|
### `block_manager_background_tranquility`
|
||||||
|
|
||||||
This parameter can be used to tune the capacity of the cache used by
|
This parameter tunes the activity of the background worker responsible for
|
||||||
[sled](https://sled.rs), the database Garage uses internally to store metadata.
|
resyncing data blocks between nodes. The higher the tranquility value is set,
|
||||||
Tune this to fit the RAM you wish to make available to your Garage instance.
|
the more the background worker will wait between iterations, meaning the load
|
||||||
This value has a conservative default (128MB) so that Garage doesn't use too much
|
on the system (including network usage between nodes) will be reduced. The
|
||||||
RAM by default, but feel free to increase this for higher performance.
|
minimal value for this parameter is `0`, where the background worker will
|
||||||
|
allways work at maximal throughput to resynchronize blocks. The default value
|
||||||
### `sled_flush_every_ms`
|
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 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`
|
### `replication_mode`
|
||||||
|
|
||||||
|
@ -299,58 +245,48 @@ 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
|
key will be returned by `garage node id` and you will have to add the IP
|
||||||
yourself.
|
yourself.
|
||||||
|
|
||||||
|
### `consul_host` and `consul_service_name`
|
||||||
## The `[consul_discovery]` section
|
|
||||||
|
|
||||||
Garage supports discovering other nodes of the cluster using Consul. For this
|
Garage supports discovering other nodes of the cluster using Consul. For this
|
||||||
to work correctly, nodes need to know their IP address by which they can be
|
to work correctly, nodes need to know their IP address by which they can be
|
||||||
reached by other nodes of the cluster, which should be set in `rpc_public_addr`.
|
reached by other nodes of the cluster, which should be set in `rpc_public_addr`.
|
||||||
|
|
||||||
### `consul_http_addr` and `service_name`
|
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
|
||||||
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.
|
RPC ports are announced.
|
||||||
|
|
||||||
### `client_cert`, `client_key`
|
Garage does not yet support talking to Consul over TLS.
|
||||||
|
|
||||||
TLS client certificate and client key to use when communicating with Consul over TLS. Both are mandatory when doing so.
|
### `kubernetes_namespace`, `kubernetes_service_name` and `kubernetes_skip_crd`
|
||||||
|
|
||||||
### `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
|
Garage supports discovering other nodes of the cluster using kubernetes custom
|
||||||
resources. For this to work, a `[kubernetes_discovery]` section must be present
|
resources. For this to work `kubernetes_namespace` and `kubernetes_service_name`
|
||||||
with at least the `namespace` and `service_name` parameters.
|
need to be configured.
|
||||||
|
|
||||||
### `namespace`
|
`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` 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.
|
filter them, to allow for multiple deployments in a single namespace.
|
||||||
|
|
||||||
### `skip_crd`
|
`kubernetes_skip_crd` can be set to true to disable the automatic creation and
|
||||||
|
|
||||||
`skip_crd` can be set to true to disable the automatic creation and
|
|
||||||
patching of the `garagenodes.deuxfleurs.fr` CRD. You will need to create the CRD
|
patching of the `garagenodes.deuxfleurs.fr` CRD. You will need to create the CRD
|
||||||
manually.
|
manually.
|
||||||
|
|
||||||
|
### `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
|
## The `[s3_api]` section
|
||||||
|
|
||||||
|
|
|
@ -1,125 +0,0 @@
|
||||||
+++
|
|
||||||
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.
|
|
||||||
Functionality 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,6 +1,6 @@
|
||||||
+++
|
+++
|
||||||
title = "K2V"
|
title = "K2V"
|
||||||
weight = 70
|
weight = 30
|
||||||
+++
|
+++
|
||||||
|
|
||||||
Starting with version 0.7.2, Garage introduces an optionnal feature, K2V,
|
Starting with version 0.7.2, Garage introduces an optionnal feature, K2V,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
+++
|
+++
|
||||||
title = "Cluster layout management"
|
title = "Cluster layout management"
|
||||||
weight = 50
|
weight = 10
|
||||||
+++
|
+++
|
||||||
|
|
||||||
The cluster layout in Garage is a table that assigns to each node a role in
|
The cluster layout in Garage is a table that assigns to each node a role in
|
||||||
|
|
45
doc/book/reference-manual/routing.md
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
+++
|
||||||
|
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"
|
title = "S3 Compatibility status"
|
||||||
weight = 40
|
weight = 20
|
||||||
+++
|
+++
|
||||||
|
|
||||||
## DISCLAIMER
|
## DISCLAIMER
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
+++
|
+++
|
||||||
title = "Working Documents"
|
title = "Working Documents"
|
||||||
weight = 8
|
weight = 7
|
||||||
sort_by = "weight"
|
sort_by = "weight"
|
||||||
template = "documentation.html"
|
template = "documentation.html"
|
||||||
+++
|
+++
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
+++
|
+++
|
||||||
title = "Design draft (obsolete)"
|
title = "Design draft"
|
||||||
weight = 900
|
weight = 25
|
||||||
+++
|
+++
|
||||||
|
|
||||||
**WARNING: this documentation is a design draft which was written before Garage's actual implementation.
|
**WARNING: this documentation is a design draft which was written before Garage's actual implementation.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
+++
|
+++
|
||||||
title = "Load balancing data (obsolete)"
|
title = "Load balancing data"
|
||||||
weight = 910
|
weight = 10
|
||||||
+++
|
+++
|
||||||
|
|
||||||
**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.**
|
**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.**
|
||||||
|
|
|
@ -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`,
|
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
|
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.)
|
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
|
these endpoints but you can change the port number or stop your reverse
|
||||||
proxy for instance.
|
proxy for instance.
|
||||||
3. Check once again that your cluster is healty. Run again `garage repair --all-nodes --yes tables` which is quick.
|
3. Check once again that your cluster is healty. Run again `garage repair --all-nodes --yes tables` which is quick.
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
+++
|
|
||||||
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.
|
|
|
@ -1,75 +0,0 @@
|
||||||
+++
|
|
||||||
title = "Testing strategy"
|
|
||||||
weight = 30
|
|
||||||
+++
|
|
||||||
|
|
||||||
|
|
||||||
## Testing Garage
|
|
||||||
|
|
||||||
Currently, we have the following tests:
|
|
||||||
|
|
||||||
- some unit tests spread around the codebase
|
|
||||||
- integration tests written in Rust (`src/garage/test`) to check that Garage operations perform correctly
|
|
||||||
- integration test for compatibility with external tools (`script/test-smoke.sh`)
|
|
||||||
|
|
||||||
We have also tried `minio/mint` but it fails a lot and for now we haven't gotten a lot from it.
|
|
||||||
|
|
||||||
In the future:
|
|
||||||
|
|
||||||
1. We'd like to have a systematic way of testing with `minio/mint`,
|
|
||||||
it would add value to Garage by providing a compatibility score and reference that can be trusted.
|
|
||||||
2. We'd also like to do testing with Jepsen in some way.
|
|
||||||
|
|
||||||
## How to instrument Garagae
|
|
||||||
|
|
||||||
We should try to test in least invasive ways, i.e. minimize the impact of the testing framework on Garage's source code. This means for example:
|
|
||||||
|
|
||||||
- Not abstracting IO/nondeterminism in the source code
|
|
||||||
- Not making `garage` a shared library (launch using `execve`, it's perfectly fine)
|
|
||||||
|
|
||||||
Instead, we should focus on building a clean outer interface for the `garage` binary,
|
|
||||||
for example loading configuration using environnement variables instead of the configuration file if that's helpfull for writing the tests.
|
|
||||||
|
|
||||||
There are two reasons for this:
|
|
||||||
|
|
||||||
- Keep the soure code clean and focused
|
|
||||||
- Test something that is as close as possible as the true garage that will actually be running
|
|
||||||
|
|
||||||
Reminder: rules of simplicity, concerning changes to Garage's source code.
|
|
||||||
Always question what we are doing.
|
|
||||||
Never do anything just because it looks nice or because we "think" it might be usefull at some later point but without knowing precisely why/when.
|
|
||||||
Only do things that make perfect sense in the context of what we currently know.
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
Testing is a research field on its own.
|
|
||||||
About testing distributed systems:
|
|
||||||
|
|
||||||
- [Jepsen](https://jepsen.io/) is a testing framework designed to test distributed systems. It can mock some part of the system like the time and the network.
|
|
||||||
- [FoundationDB Testing Approach](https://www.micahlerner.com/2021/06/12/foundationdb-a-distributed-unbundled-transactional-key-value-store.html#what-is-unique-about-foundationdbs-testing-framework). They chose to abstract "all sources of nondeterminism and communication are abstracted, including network, disk, time, and pseudo random number generator" to be able to run tests by simulating faults.
|
|
||||||
- [Testing Distributed Systems](https://asatarin.github.io/testing-distributed-systems/) - Curated list of resources on testing distributed systems
|
|
||||||
|
|
||||||
About S3 compatibility:
|
|
||||||
- [ceph/s3-tests](https://github.com/ceph/s3-tests)
|
|
||||||
- (deprecated) [minio/s3verify](https://blog.min.io/s3verify-a-simple-tool-to-verify-aws-s3-api-compatibility/)
|
|
||||||
- [minio/mint](https://github.com/minio/mint)
|
|
||||||
|
|
||||||
About benchmarking S3 (I think it is not necessarily very relevant for this iteration):
|
|
||||||
- [minio/warp](https://github.com/minio/warp)
|
|
||||||
- [wasabi-tech/s3-benchmark](https://github.com/wasabi-tech/s3-benchmark)
|
|
||||||
- [dvassallo/s3-benchmark](https://github.com/dvassallo/s3-benchmark)
|
|
||||||
- [intel-cloud/cosbench](https://github.com/intel-cloud/cosbench) - used by Ceph
|
|
||||||
|
|
||||||
Engineering blog posts:
|
|
||||||
- [Quincy @ Scale: A Tale of Three Large-Scale Clusters](https://ceph.io/en/news/blog/2022/three-large-scale-clusters/)
|
|
||||||
|
|
||||||
Interesting blog posts on the blog of the Sled database:
|
|
||||||
|
|
||||||
- <https://sled.rs/simulation.html>
|
|
||||||
- <https://sled.rs/perf.html>
|
|
||||||
|
|
||||||
Misc:
|
|
||||||
- [mutagen](https://github.com/llogiq/mutagen) - mutation testing is a way to assert our test quality by mutating the code and see if the mutation makes the tests fail
|
|
||||||
- [fuzzing](https://rust-fuzz.github.io/book/) - cargo supports fuzzing, it could be a way to test our software reliability in presence of garbage data.
|
|
||||||
|
|
||||||
|
|
|
@ -1,686 +0,0 @@
|
||||||
+++
|
|
||||||
title = "Administration API"
|
|
||||||
weight = 60
|
|
||||||
+++
|
|
||||||
|
|
||||||
The Garage administration API is accessible through a dedicated server whose
|
|
||||||
listen address is specified in the `[admin]` section of the configuration
|
|
||||||
file (see [configuration file
|
|
||||||
reference](@/documentation/reference-manual/configuration.md))
|
|
||||||
|
|
||||||
**WARNING.** At this point, there is no comittement to stability of the APIs described in this document.
|
|
||||||
We will bump the version numbers prefixed to each API endpoint at each time the syntax
|
|
||||||
or semantics change, meaning that code that relies on these endpoint will break
|
|
||||||
when changes are introduced.
|
|
||||||
|
|
||||||
The Garage administration API was introduced in version 0.7.2, this document
|
|
||||||
does not apply to older versions of Garage.
|
|
||||||
|
|
||||||
|
|
||||||
## Access control
|
|
||||||
|
|
||||||
The admin API uses two different tokens for acces control, that are specified in the config file's `[admin]` section:
|
|
||||||
|
|
||||||
- `metrics_token`: the token for accessing the Metrics endpoint (if this token
|
|
||||||
is not set in the config file, the Metrics endpoint can be accessed without
|
|
||||||
access control);
|
|
||||||
|
|
||||||
- `admin_token`: the token for accessing all of the other administration
|
|
||||||
endpoints (if this token is not set in the config file, access to these
|
|
||||||
endpoints is disabled entirely).
|
|
||||||
|
|
||||||
These tokens are used as simple HTTP bearer tokens. In other words, to
|
|
||||||
authenticate access to an admin API endpoint, add the following HTTP header
|
|
||||||
to your request:
|
|
||||||
|
|
||||||
```
|
|
||||||
Authorization: Bearer <token>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Administration API endpoints
|
|
||||||
|
|
||||||
### Metrics-related endpoints
|
|
||||||
|
|
||||||
#### Metrics `GET /metrics`
|
|
||||||
|
|
||||||
Returns internal Garage metrics in Prometheus format.
|
|
||||||
|
|
||||||
#### Health `GET /health`
|
|
||||||
|
|
||||||
Used for simple health checks in a cluster setting with an orchestrator.
|
|
||||||
Returns an HTTP status 200 if the node is ready to answer user's requests,
|
|
||||||
and an HTTP status 503 (Service Unavailable) if there are some partitions
|
|
||||||
for which a quorum of nodes is not available.
|
|
||||||
A simple textual message is also returned in a body with content-type `text/plain`.
|
|
||||||
See `/v0/health` for an API that also returns JSON output.
|
|
||||||
|
|
||||||
### Cluster operations
|
|
||||||
|
|
||||||
#### GetClusterStatus `GET /v0/status`
|
|
||||||
|
|
||||||
Returns the cluster's current status in JSON, including:
|
|
||||||
|
|
||||||
- ID of the node being queried and its version of the Garage daemon
|
|
||||||
- Live nodes
|
|
||||||
- Currently configured cluster layout
|
|
||||||
- Staged changes to the cluster layout
|
|
||||||
|
|
||||||
Example response body:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"node": "ec79480e0ce52ae26fd00c9da684e4fa56658d9c64cdcecb094e936de0bfe71f",
|
|
||||||
"garage_version": "git:v0.8.0",
|
|
||||||
"knownNodes": {
|
|
||||||
"ec79480e0ce52ae26fd00c9da684e4fa56658d9c64cdcecb094e936de0bfe71f": {
|
|
||||||
"addr": "10.0.0.11:3901",
|
|
||||||
"is_up": true,
|
|
||||||
"last_seen_secs_ago": 9,
|
|
||||||
"hostname": "node1"
|
|
||||||
},
|
|
||||||
"4a6ae5a1d0d33bf895f5bb4f0a418b7dc94c47c0dd2eb108d1158f3c8f60b0ff": {
|
|
||||||
"addr": "10.0.0.12:3901",
|
|
||||||
"is_up": true,
|
|
||||||
"last_seen_secs_ago": 1,
|
|
||||||
"hostname": "node2"
|
|
||||||
},
|
|
||||||
"23ffd0cdd375ebff573b20cc5cef38996b51c1a7d6dbcf2c6e619876e507cf27": {
|
|
||||||
"addr": "10.0.0.21:3901",
|
|
||||||
"is_up": true,
|
|
||||||
"last_seen_secs_ago": 7,
|
|
||||||
"hostname": "node3"
|
|
||||||
},
|
|
||||||
"e2ee7984ee65b260682086ec70026165903c86e601a4a5a501c1900afe28d84b": {
|
|
||||||
"addr": "10.0.0.22:3901",
|
|
||||||
"is_up": true,
|
|
||||||
"last_seen_secs_ago": 1,
|
|
||||||
"hostname": "node4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"layout": {
|
|
||||||
"version": 12,
|
|
||||||
"roles": {
|
|
||||||
"ec79480e0ce52ae26fd00c9da684e4fa56658d9c64cdcecb094e936de0bfe71f": {
|
|
||||||
"zone": "dc1",
|
|
||||||
"capacity": 4,
|
|
||||||
"tags": [
|
|
||||||
"node1"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"4a6ae5a1d0d33bf895f5bb4f0a418b7dc94c47c0dd2eb108d1158f3c8f60b0ff": {
|
|
||||||
"zone": "dc1",
|
|
||||||
"capacity": 6,
|
|
||||||
"tags": [
|
|
||||||
"node2"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"23ffd0cdd375ebff573b20cc5cef38996b51c1a7d6dbcf2c6e619876e507cf27": {
|
|
||||||
"zone": "dc2",
|
|
||||||
"capacity": 10,
|
|
||||||
"tags": [
|
|
||||||
"node3"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"stagedRoleChanges": {
|
|
||||||
"e2ee7984ee65b260682086ec70026165903c86e601a4a5a501c1900afe28d84b": {
|
|
||||||
"zone": "dc2",
|
|
||||||
"capacity": 5,
|
|
||||||
"tags": [
|
|
||||||
"node4"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### GetClusterHealth `GET /v0/health`
|
|
||||||
|
|
||||||
Returns the cluster's current health in JSON format, with the following variables:
|
|
||||||
|
|
||||||
- `status`: one of `Healthy`, `Degraded` or `Unavailable`:
|
|
||||||
- Healthy: Garage node is connected to all storage nodes
|
|
||||||
- Degraded: Garage node is not connected to all storage nodes, but a quorum of write nodes is available for all partitions
|
|
||||||
- Unavailable: a quorum of write nodes is not available for some partitions
|
|
||||||
- `known_nodes`: the number of nodes this Garage node has had a TCP connection to since the daemon started
|
|
||||||
- `connected_nodes`: the nubmer of nodes this Garage node currently has an open connection to
|
|
||||||
- `storage_nodes`: the number of storage nodes currently registered in the cluster layout
|
|
||||||
- `storage_nodes_ok`: the number of storage nodes to which a connection is currently open
|
|
||||||
- `partitions`: the total number of partitions of the data (currently always 256)
|
|
||||||
- `partitions_quorum`: the number of partitions for which a quorum of write nodes is available
|
|
||||||
- `partitions_all_ok`: the number of partitions for which we are connected to all storage nodes responsible of storing it
|
|
||||||
|
|
||||||
Contrarily to `GET /health`, this endpoint always returns a 200 OK HTTP response code.
|
|
||||||
|
|
||||||
Example response body:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"status": "Degraded",
|
|
||||||
"known_nodes": 3,
|
|
||||||
"connected_nodes": 2,
|
|
||||||
"storage_nodes": 3,
|
|
||||||
"storage_nodes_ok": 2,
|
|
||||||
"partitions": 256,
|
|
||||||
"partitions_quorum": 256,
|
|
||||||
"partitions_all_ok": 0
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### ConnectClusterNodes `POST /v0/connect`
|
|
||||||
|
|
||||||
Instructs this Garage node to connect to other Garage nodes at specified addresses.
|
|
||||||
|
|
||||||
Example request body:
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
"ec79480e0ce52ae26fd00c9da684e4fa56658d9c64cdcecb094e936de0bfe71f@10.0.0.11:3901",
|
|
||||||
"4a6ae5a1d0d33bf895f5bb4f0a418b7dc94c47c0dd2eb108d1158f3c8f60b0ff@10.0.0.12:3901"
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
The format of the string for a node to connect to is: `<node ID>@<ip address>:<port>`, same as in the `garage node connect` CLI call.
|
|
||||||
|
|
||||||
Example response:
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"error": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"success": false,
|
|
||||||
"error": "Handshake error"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
#### GetClusterLayout `GET /v0/layout`
|
|
||||||
|
|
||||||
Returns the cluster's current layout in JSON, including:
|
|
||||||
|
|
||||||
- Currently configured cluster layout
|
|
||||||
- Staged changes to the cluster layout
|
|
||||||
|
|
||||||
(the info returned by this endpoint is a subset of the info returned by GetClusterStatus)
|
|
||||||
|
|
||||||
Example response body:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"version": 12,
|
|
||||||
"roles": {
|
|
||||||
"ec79480e0ce52ae26fd00c9da684e4fa56658d9c64cdcecb094e936de0bfe71f": {
|
|
||||||
"zone": "dc1",
|
|
||||||
"capacity": 4,
|
|
||||||
"tags": [
|
|
||||||
"node1"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"4a6ae5a1d0d33bf895f5bb4f0a418b7dc94c47c0dd2eb108d1158f3c8f60b0ff": {
|
|
||||||
"zone": "dc1",
|
|
||||||
"capacity": 6,
|
|
||||||
"tags": [
|
|
||||||
"node2"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"23ffd0cdd375ebff573b20cc5cef38996b51c1a7d6dbcf2c6e619876e507cf27": {
|
|
||||||
"zone": "dc2",
|
|
||||||
"capacity": 10,
|
|
||||||
"tags": [
|
|
||||||
"node3"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"stagedRoleChanges": {
|
|
||||||
"e2ee7984ee65b260682086ec70026165903c86e601a4a5a501c1900afe28d84b": {
|
|
||||||
"zone": "dc2",
|
|
||||||
"capacity": 5,
|
|
||||||
"tags": [
|
|
||||||
"node4"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### UpdateClusterLayout `POST /v0/layout`
|
|
||||||
|
|
||||||
Send modifications to the cluster layout. These modifications will
|
|
||||||
be included in the staged role changes, visible in subsequent calls
|
|
||||||
of `GetClusterLayout`. Once the set of staged changes is satisfactory,
|
|
||||||
the user may call `ApplyClusterLayout` to apply the changed changes,
|
|
||||||
or `Revert ClusterLayout` to clear all of the staged changes in
|
|
||||||
the layout.
|
|
||||||
|
|
||||||
Request body format:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
<node_id>: {
|
|
||||||
"capacity": <new_capacity>,
|
|
||||||
"zone": <new_zone>,
|
|
||||||
"tags": [
|
|
||||||
<new_tag>,
|
|
||||||
...
|
|
||||||
]
|
|
||||||
},
|
|
||||||
<node_id_to_remove>: null,
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Contrary to the CLI that may update only a subset of the fields
|
|
||||||
`capacity`, `zone` and `tags`, when calling this API all of these
|
|
||||||
values must be specified.
|
|
||||||
|
|
||||||
|
|
||||||
#### ApplyClusterLayout `POST /v0/layout/apply`
|
|
||||||
|
|
||||||
Applies to the cluster the layout changes currently registered as
|
|
||||||
staged layout changes.
|
|
||||||
|
|
||||||
Request body format:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"version": 13
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Similarly to the CLI, the body must include the version of the new layout
|
|
||||||
that will be created, which MUST be 1 + the value of the currently
|
|
||||||
existing layout in the cluster.
|
|
||||||
|
|
||||||
#### RevertClusterLayout `POST /v0/layout/revert`
|
|
||||||
|
|
||||||
Clears all of the staged layout changes.
|
|
||||||
|
|
||||||
Request body format:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"version": 13
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Reverting the staged changes is done by incrementing the version number
|
|
||||||
and clearing the contents of the staged change list.
|
|
||||||
Similarly to the CLI, the body must include the incremented
|
|
||||||
version number, which MUST be 1 + the value of the currently
|
|
||||||
existing layout in the cluster.
|
|
||||||
|
|
||||||
|
|
||||||
### Access key operations
|
|
||||||
|
|
||||||
#### ListKeys `GET /v0/key`
|
|
||||||
|
|
||||||
Returns all API access keys in the cluster.
|
|
||||||
|
|
||||||
Example response:
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"id": "GK31c2f218a2e44f485b94239e",
|
|
||||||
"name": "test"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "GKe10061ac9c2921f09e4c5540",
|
|
||||||
"name": "test2"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
#### CreateKey `POST /v0/key`
|
|
||||||
|
|
||||||
Creates a new API access key.
|
|
||||||
|
|
||||||
Request body format:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "NameOfMyKey"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### ImportKey `POST /v0/key/import`
|
|
||||||
|
|
||||||
Imports an existing API key.
|
|
||||||
|
|
||||||
Request body format:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"accessKeyId": "GK31c2f218a2e44f485b94239e",
|
|
||||||
"secretAccessKey": "b892c0665f0ada8a4755dae98baa3b133590e11dae3bcc1f9d769d67f16c3835",
|
|
||||||
"name": "NameOfMyKey"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### GetKeyInfo `GET /v0/key?id=<acces key id>`
|
|
||||||
#### GetKeyInfo `GET /v0/key?search=<pattern>`
|
|
||||||
|
|
||||||
Returns information about the requested API access key.
|
|
||||||
|
|
||||||
If `id` is set, the key is looked up using its exact identifier (faster).
|
|
||||||
If `search` is set, the key is looked up using its name or prefix
|
|
||||||
of identifier (slower, all keys are enumerated to do this).
|
|
||||||
|
|
||||||
Example response:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "test",
|
|
||||||
"accessKeyId": "GK31c2f218a2e44f485b94239e",
|
|
||||||
"secretAccessKey": "b892c0665f0ada8a4755dae98baa3b133590e11dae3bcc1f9d769d67f16c3835",
|
|
||||||
"permissions": {
|
|
||||||
"createBucket": false
|
|
||||||
},
|
|
||||||
"buckets": [
|
|
||||||
{
|
|
||||||
"id": "70dc3bed7fe83a75e46b66e7ddef7d56e65f3c02f9f80b6749fb97eccb5e1033",
|
|
||||||
"globalAliases": [
|
|
||||||
"test2"
|
|
||||||
],
|
|
||||||
"localAliases": [],
|
|
||||||
"permissions": {
|
|
||||||
"read": true,
|
|
||||||
"write": true,
|
|
||||||
"owner": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "d7452a935e663fc1914f3a5515163a6d3724010ce8dfd9e4743ca8be5974f995",
|
|
||||||
"globalAliases": [
|
|
||||||
"test3"
|
|
||||||
],
|
|
||||||
"localAliases": [],
|
|
||||||
"permissions": {
|
|
||||||
"read": true,
|
|
||||||
"write": true,
|
|
||||||
"owner": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "e6a14cd6a27f48684579ec6b381c078ab11697e6bc8513b72b2f5307e25fff9b",
|
|
||||||
"globalAliases": [],
|
|
||||||
"localAliases": [
|
|
||||||
"test"
|
|
||||||
],
|
|
||||||
"permissions": {
|
|
||||||
"read": true,
|
|
||||||
"write": true,
|
|
||||||
"owner": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "96470e0df00ec28807138daf01915cfda2bee8eccc91dea9558c0b4855b5bf95",
|
|
||||||
"globalAliases": [
|
|
||||||
"alex"
|
|
||||||
],
|
|
||||||
"localAliases": [],
|
|
||||||
"permissions": {
|
|
||||||
"read": true,
|
|
||||||
"write": true,
|
|
||||||
"owner": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### DeleteKey `DELETE /v0/key?id=<acces key id>`
|
|
||||||
|
|
||||||
Deletes an API access key.
|
|
||||||
|
|
||||||
#### UpdateKey `POST /v0/key?id=<acces key id>`
|
|
||||||
|
|
||||||
Updates information about the specified API access key.
|
|
||||||
|
|
||||||
Request body format:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "NameOfMyKey",
|
|
||||||
"allow": {
|
|
||||||
"createBucket": true,
|
|
||||||
},
|
|
||||||
"deny": {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
All fields (`name`, `allow` and `deny`) are optionnal.
|
|
||||||
If they are present, the corresponding modifications are applied to the key, otherwise nothing is changed.
|
|
||||||
The possible flags in `allow` and `deny` are: `createBucket`.
|
|
||||||
|
|
||||||
|
|
||||||
### Bucket operations
|
|
||||||
|
|
||||||
#### ListBuckets `GET /v0/bucket`
|
|
||||||
|
|
||||||
Returns all storage buckets in the cluster.
|
|
||||||
|
|
||||||
Example response:
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"id": "70dc3bed7fe83a75e46b66e7ddef7d56e65f3c02f9f80b6749fb97eccb5e1033",
|
|
||||||
"globalAliases": [
|
|
||||||
"test2"
|
|
||||||
],
|
|
||||||
"localAliases": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "96470e0df00ec28807138daf01915cfda2bee8eccc91dea9558c0b4855b5bf95",
|
|
||||||
"globalAliases": [
|
|
||||||
"alex"
|
|
||||||
],
|
|
||||||
"localAliases": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "d7452a935e663fc1914f3a5515163a6d3724010ce8dfd9e4743ca8be5974f995",
|
|
||||||
"globalAliases": [
|
|
||||||
"test3"
|
|
||||||
],
|
|
||||||
"localAliases": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "e6a14cd6a27f48684579ec6b381c078ab11697e6bc8513b72b2f5307e25fff9b",
|
|
||||||
"globalAliases": [],
|
|
||||||
"localAliases": [
|
|
||||||
{
|
|
||||||
"accessKeyId": "GK31c2f218a2e44f485b94239e",
|
|
||||||
"alias": "test"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
#### GetBucketInfo `GET /v0/bucket?id=<bucket id>`
|
|
||||||
#### GetBucketInfo `GET /v0/bucket?globalAlias=<alias>`
|
|
||||||
|
|
||||||
Returns information about the requested storage bucket.
|
|
||||||
|
|
||||||
If `id` is set, the bucket is looked up using its exact identifier.
|
|
||||||
If `globalAlias` is set, the bucket is looked up using its global alias.
|
|
||||||
(both are fast)
|
|
||||||
|
|
||||||
Example response:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### CreateBucket `POST /v0/bucket`
|
|
||||||
|
|
||||||
Creates a new storage bucket.
|
|
||||||
|
|
||||||
Request body format:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"globalAlias": "NameOfMyBucket"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
OR
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"localAlias": {
|
|
||||||
"accessKeyId": "GK31c2f218a2e44f485b94239e",
|
|
||||||
"alias": "NameOfMyBucket",
|
|
||||||
"allow": {
|
|
||||||
"read": true,
|
|
||||||
"write": true,
|
|
||||||
"owner": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
OR
|
|
||||||
|
|
||||||
```json
|
|
||||||
{}
|
|
||||||
```
|
|
||||||
|
|
||||||
Creates a new bucket, either with a global alias, a local one,
|
|
||||||
or no alias at all.
|
|
||||||
|
|
||||||
Technically, you can also specify both `globalAlias` and `localAlias` and that would create
|
|
||||||
two aliases, but I don't see why you would want to do that.
|
|
||||||
|
|
||||||
#### DeleteBucket `DELETE /v0/bucket?id=<bucket id>`
|
|
||||||
|
|
||||||
Deletes a storage bucket. A bucket cannot be deleted if it is not empty.
|
|
||||||
|
|
||||||
Warning: this will delete all aliases associated with the bucket!
|
|
||||||
|
|
||||||
#### UpdateBucket `PUT /v0/bucket?id=<bucket id>`
|
|
||||||
|
|
||||||
Updates configuration of the given bucket.
|
|
||||||
|
|
||||||
Request body format:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"websiteAccess": {
|
|
||||||
"enabled": true,
|
|
||||||
"indexDocument": "index.html",
|
|
||||||
"errorDocument": "404.html"
|
|
||||||
},
|
|
||||||
"quotas": {
|
|
||||||
"maxSize": 19029801,
|
|
||||||
"maxObjects": null,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
#### BucketAllowKey `POST /v0/bucket/allow`
|
|
||||||
|
|
||||||
Allows a key to do read/write/owner operations on a bucket.
|
|
||||||
|
|
||||||
Request body format:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"bucketId": "e6a14cd6a27f48684579ec6b381c078ab11697e6bc8513b72b2f5307e25fff9b",
|
|
||||||
"accessKeyId": "GK31c2f218a2e44f485b94239e",
|
|
||||||
"permissions": {
|
|
||||||
"read": true,
|
|
||||||
"write": true,
|
|
||||||
"owner": true
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Flags in `permissions` which have the value `true` will be activated.
|
|
||||||
Other flags will remain unchanged.
|
|
||||||
|
|
||||||
#### BucketDenyKey `POST /v0/bucket/deny`
|
|
||||||
|
|
||||||
Denies a key from doing read/write/owner operations on a bucket.
|
|
||||||
|
|
||||||
Request body format:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"bucketId": "e6a14cd6a27f48684579ec6b381c078ab11697e6bc8513b72b2f5307e25fff9b",
|
|
||||||
"accessKeyId": "GK31c2f218a2e44f485b94239e",
|
|
||||||
"permissions": {
|
|
||||||
"read": false,
|
|
||||||
"write": false,
|
|
||||||
"owner": true
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Flags in `permissions` which have the value `true` will be deactivated.
|
|
||||||
Other flags will remain unchanged.
|
|
||||||
|
|
||||||
|
|
||||||
### Operations on bucket aliases
|
|
||||||
|
|
||||||
#### GlobalAliasBucket `PUT /v0/bucket/alias/global?id=<bucket id>&alias=<global alias>`
|
|
||||||
|
|
||||||
Empty body. Creates a global alias for a bucket.
|
|
||||||
|
|
||||||
#### GlobalUnaliasBucket `DELETE /v0/bucket/alias/global?id=<bucket id>&alias=<global alias>`
|
|
||||||
|
|
||||||
Removes a global alias for a bucket.
|
|
||||||
|
|
||||||
#### LocalAliasBucket `PUT /v0/bucket/alias/local?id=<bucket id>&accessKeyId=<access key ID>&alias=<local alias>`
|
|
||||||
|
|
||||||
Empty body. Creates a local alias for a bucket in the namespace of a specific access key.
|
|
||||||
|
|
||||||
#### LocalUnaliasBucket `DELETE /v0/bucket/alias/local?id=<bucket id>&accessKeyId<access key ID>&alias=<local alias>`
|
|
||||||
|
|
||||||
Removes a local alias for a bucket in the namespace of a specific access key.
|
|
||||||
|
|
|
@ -206,8 +206,8 @@ and responses need to be translated.
|
||||||
|
|
||||||
Query parameters:
|
Query parameters:
|
||||||
|
|
||||||
| name | default value | meaning |
|
| name | default value | meaning |
|
||||||
|------------|---------------|----------------------------------|
|
| - | - | - |
|
||||||
| `sort_key` | **mandatory** | The sort key of the item to read |
|
| `sort_key` | **mandatory** | The sort key of the item to read |
|
||||||
|
|
||||||
Returns the item with specified partition key and sort key. Values can be
|
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:
|
Query parameters:
|
||||||
|
|
||||||
| name | default value | meaning |
|
| name | default value | meaning |
|
||||||
|-------------------|---------------|----------------------------------------------------------------------------|
|
| - | - | - |
|
||||||
| `sort_key` | **mandatory** | The sort key of the item to read |
|
| `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 |
|
| `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 |
|
| `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).
|
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:
|
Example response:
|
||||||
|
|
||||||
```
|
```
|
||||||
HTTP/1.1 204 No Content
|
HTTP/1.1 200 OK
|
||||||
```
|
```
|
||||||
|
|
||||||
**DeleteItem: `DELETE /<bucket>/<partition key>?sort_key=<sort_key>`**
|
**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:
|
Query parameters:
|
||||||
|
|
||||||
| name | default value | meaning |
|
| name | default value | meaning |
|
||||||
|-----------|---------------|----------------------------------------------------------------|
|
| - | - | - |
|
||||||
| `prefix` | `null` | Restrict listing to partition keys that start with this prefix |
|
| `prefix` | `null` | Restrict listing to partition keys that start with this prefix |
|
||||||
| `start` | `null` | First partition key to list, in lexicographical order |
|
| `start` | `null` | First partition key to list, in lexicographical order |
|
||||||
| `end` | `null` | Last partition key to list (excluded) |
|
| `end` | `null` | Last partition key to list (excluded) |
|
||||||
| `limit` | `null` | Maximum number of partition keys to list |
|
| `limit` | `null` | Maximum number of partition keys to list |
|
||||||
| `reverse` | `false` | Iterate in reverse lexicographical order |
|
| `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).
|
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:
|
Example response:
|
||||||
|
|
||||||
```
|
```
|
||||||
HTTP/1.1 204 NO CONTENT
|
HTTP/1.1 200 OK
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
items to get (to get single items, set `singleItem` to `true`). A search is a
|
||||||
JSON struct with the following fields:
|
JSON struct with the following fields:
|
||||||
|
|
||||||
| name | default value | meaning |
|
| name | default value | meaning |
|
||||||
|-----------------|---------------|----------------------------------------------------------------------------------------|
|
| - | - | - |
|
||||||
| `partitionKey` | **mandatory** | The partition key in which to search |
|
| `partitionKey` | **mandatory** | The partition key in which to search |
|
||||||
| `prefix` | `null` | Restrict items to list to those whose sort keys start with this prefix |
|
| `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 |
|
| `start` | `null` | The sort key of the first item to read |
|
||||||
| `end` | `null` | The sort key of the last item to read (excluded) |
|
| `end` | `null` | The sort key of the last item to read (excluded) |
|
||||||
| `limit` | `null` | The maximum number of items to return |
|
| `limit` | `null` | The maximum number of items to return |
|
||||||
| `reverse` | `false` | Iterate in reverse lexicographical order on sort keys |
|
| `reverse` | `false` | Iterate in reverse lexicographical order on sort keys |
|
||||||
| `singleItem` | `false` | Whether to return only the item with sort key `start` |
|
| `singleItem` | `false` | Whether to return only the item with sort key `start` |
|
||||||
| `conflictsOnly` | `false` | Whether to return only items that have several concurrent values |
|
| `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 |
|
| `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
|
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:
|
Example response:
|
||||||
|
|
||||||
```json
|
```
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
|
|
||||||
[
|
[
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
*.aux
|
|
||||||
*.bbl
|
|
||||||
*.blg
|
|
||||||
*.log
|
|
||||||
*.nav
|
|
||||||
*.out
|
|
||||||
*.snm
|
|
||||||
*.synctex.gz
|
|
||||||
*.toc
|
|
||||||
*.dvi
|
|
|
@ -1,8 +0,0 @@
|
||||||
all:
|
|
||||||
pdflatex présentation.tex
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f *.aux *.bbl *.blg *.log *.nav *.out *.snm *.synctex.gz *.toc *.dvi présentation.pdf
|
|
||||||
|
|
||||||
clean_sauf_pdf:
|
|
||||||
rm -f *.aux *.bbl *.blg *.log *.nav *.out *.snm *.synctex.gz *.toc *.dvi
|
|
Before Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 196 KiB |
Before Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 199 KiB |
Before Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 174 KiB |
Before Width: | Height: | Size: 126 KiB |
Before Width: | Height: | Size: 20 KiB |
|
@ -1,340 +0,0 @@
|
||||||
\documentclass[11pt, aspectratio=1610]{beamer}
|
|
||||||
\usetheme{Warsaw}
|
|
||||||
\usepackage[utf8]{inputenc}
|
|
||||||
\usepackage[french]{babel}
|
|
||||||
\usepackage{amsmath}
|
|
||||||
\usepackage{amsfonts}
|
|
||||||
\usepackage{amssymb}
|
|
||||||
\usepackage{tikz}
|
|
||||||
\usepackage{graphicx}
|
|
||||||
\usepackage{xcolor}
|
|
||||||
\usepackage{setspace}
|
|
||||||
\usepackage{todonotes}
|
|
||||||
\presetkeys{todonotes}{inline}{}
|
|
||||||
\renewcommand{\baselinestretch}{1.25}
|
|
||||||
|
|
||||||
\definecolor{orange_garage}{RGB}{255,147,41}
|
|
||||||
\definecolor{gris_garage}{RGB}{78,78,78}
|
|
||||||
|
|
||||||
\author[Association Deuxfleurs]{~\linebreak Vincent Giraud}
|
|
||||||
\title[De l'auto-hébergement à l'entre-hébergement avec Garage]{De l'auto-hébergement à l'entre-hébergement :\\Garage, pour conserver ses données ensemble}
|
|
||||||
%\setbeamercovered{transparent}
|
|
||||||
%\setbeamertemplate{navigation symbols}{}
|
|
||||||
\date{Capitole du Libre 2022\linebreak
|
|
||||||
|
|
||||||
\scriptsize Samedi 19 novembre 2022\linebreak
|
|
||||||
}
|
|
||||||
|
|
||||||
\setbeamercolor{palette primary}{fg=gris_garage,bg=orange_garage}
|
|
||||||
\setbeamercolor{palette secondary}{fg=gris_garage,bg=gris_garage}
|
|
||||||
\setbeamercolor{palette tiertary}{fg=white,bg=gris_garage}
|
|
||||||
\setbeamercolor{palette quaternary}{fg=white,bg=gris_garage}
|
|
||||||
\setbeamercolor{navigation symbols}{fg=black, bg=white}
|
|
||||||
\setbeamercolor{navigation symbols dimmed}{fg=darkgray, bg=white}
|
|
||||||
\setbeamercolor{itemize item}{fg=gris_garage}
|
|
||||||
\setbeamertemplate{itemize item}[circle]
|
|
||||||
|
|
||||||
\addtobeamertemplate{navigation symbols}{}{%
|
|
||||||
\usebeamerfont{footline}%
|
|
||||||
\usebeamercolor[fg]{footline}%
|
|
||||||
\hspace{1em}%
|
|
||||||
\insertframenumber/\inserttotalframenumber
|
|
||||||
}
|
|
||||||
|
|
||||||
\setbeamertemplate{headline}
|
|
||||||
{%
|
|
||||||
\leavevmode%
|
|
||||||
\begin{beamercolorbox}[wd=.5\paperwidth,ht=2.5ex,dp=1.125ex]{section in head/foot}%
|
|
||||||
\hbox to .5\paperwidth{\hfil\insertsectionhead\hfil}
|
|
||||||
\end{beamercolorbox}%
|
|
||||||
\begin{beamercolorbox}[wd=.5\paperwidth,ht=2.5ex,dp=1.125ex]{subsection in head/foot}%
|
|
||||||
\hbox to .5\paperwidth{\hfil\insertsubsectionhead\hfil}
|
|
||||||
\end{beamercolorbox}%
|
|
||||||
}
|
|
||||||
\addtobeamertemplate{footnote}{}{\vspace{2ex}}
|
|
||||||
|
|
||||||
\begin{document}
|
|
||||||
\begin{frame}
|
|
||||||
\titlepage
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\section{Introduction}
|
|
||||||
\subsection{Présentation}
|
|
||||||
\begin{frame}
|
|
||||||
\begin{columns}
|
|
||||||
\column{0.5 \linewidth}
|
|
||||||
\begin{center}
|
|
||||||
\includegraphics[width=3.5cm]{deuxfleurs-logo.png}\linebreak
|
|
||||||
|
|
||||||
\texttt{https://deuxfleurs.fr}
|
|
||||||
\end{center}
|
|
||||||
\column{0.4 \linewidth}
|
|
||||||
\begin{center}
|
|
||||||
Deuxfleurs est une association militant en faveur d'un internet plus convivial, avec une organisation et des rapports de force repensés.\linebreak
|
|
||||||
|
|
||||||
Nous faisons partie du CHATONS\footnote[frame]{Collectif des Hébergeurs Alternatifs, Transparents, Ouverts, Neutres et Solidaires} depuis avril 2022.
|
|
||||||
|
|
||||||
\includegraphics[width=2cm]{logo_chatons.png}
|
|
||||||
\end{center}
|
|
||||||
\end{columns}
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\subsection{Héberger à la maison}
|
|
||||||
\begin{frame}
|
|
||||||
\begin{columns}
|
|
||||||
\begin{column}{0.5 \linewidth}
|
|
||||||
\begin{center}
|
|
||||||
Pour échapper au contrôle et au giron des opérateurs de clouds, héberger ses données à la maison présente de nombreux avantages...
|
|
||||||
\end{center}
|
|
||||||
|
|
||||||
\vspace{0.5cm}
|
|
||||||
|
|
||||||
\begin{itemize}[<+(1)->]
|
|
||||||
\item On récupère la souveraineté sur ses données
|
|
||||||
\item On gagne en vie privée
|
|
||||||
\item On gagne en libertés
|
|
||||||
\item On est responsabilisé face à ses besoins
|
|
||||||
\end{itemize}
|
|
||||||
\end{column}
|
|
||||||
\vrule{}
|
|
||||||
\begin{column}{0.5 \linewidth}
|
|
||||||
\begin{center}
|
|
||||||
\onslide<6->{... mais aussi bien des contraintes...}
|
|
||||||
\end{center}
|
|
||||||
|
|
||||||
\vspace{0.5cm}
|
|
||||||
|
|
||||||
\begin{itemize}[<+(2)->]
|
|
||||||
\item On repose sur une connexion internet pour particulier
|
|
||||||
\item Un certain savoir-faire et moultes compétences sont requis
|
|
||||||
\item Assurer la résilience de ses services est difficile
|
|
||||||
\item Bien sauvegarder ses données, et ceci au-delà de son site géographique, n'est pas évident
|
|
||||||
\end{itemize}
|
|
||||||
\end{column}
|
|
||||||
\end{columns}
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\subsection{Sauvegarder pour se parer à tout imprévu}
|
|
||||||
\begin{frame}
|
|
||||||
\begin{center}
|
|
||||||
Sauvegarder pour se parer contre les pannes matérielles est une chose...
|
|
||||||
|
|
||||||
Sauvegarder pour se parer contre les cambriolages et les incendies en est une autre !\linebreak
|
|
||||||
|
|
||||||
\vspace{1cm}
|
|
||||||
\onslide<2->{Répartir géographiquement ses données devient alors nécessaire.}
|
|
||||||
\end{center}
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\section{Les solutions à explorer}
|
|
||||||
\subsection{L'entre-hébergement}
|
|
||||||
\begin{frame}
|
|
||||||
\begin{center}
|
|
||||||
On a vu récemment se développer au sein du CHATONS la notion d'entre-hébergement : en plus de renforcer l'intégrité des sauvegardes, on va améliorer la disponibilité pendant les coupures de liaison internet, de courant, ou pendant les déménagements d'administrateurs par exemple.\linebreak
|
|
||||||
|
|
||||||
\vspace{1cm}
|
|
||||||
\onslide<2->
|
|
||||||
{
|
|
||||||
Dans le cadre du collectif, il s'agit de partager ses volumes de données entre hébergeurs.\linebreak
|
|
||||||
|
|
||||||
Pour assurer la confidentialité, on peut chiffrer les données au niveau applicatif.
|
|
||||||
}
|
|
||||||
\end{center}
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\subsection{S3 contre les systèmes de fichiers}
|
|
||||||
\begin{frame}
|
|
||||||
\begin{center}
|
|
||||||
Dans le cadre de l'administration de services en ligne, les systèmes de fichiers recèlent certaines difficultés.\linebreak
|
|
||||||
|
|
||||||
\vspace{1cm}
|
|
||||||
Le standard S3 apporte des facilités; on réduit le stockage à un paradigme de clé-valeur basé essentiellement sur deux opérations seulement: lire ou écrire une clé.
|
|
||||||
\end{center}
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\section{Garage}
|
|
||||||
\subsection{Présentation}
|
|
||||||
\begin{frame}
|
|
||||||
\begin{columns}
|
|
||||||
\column{0.5 \linewidth}
|
|
||||||
\begin{center}
|
|
||||||
Garage essaye de répondre à l'ensemble de ces besoins.\linebreak
|
|
||||||
|
|
||||||
\vspace{0.5cm}
|
|
||||||
Il s'agit d'un logiciel libre permettant de distribuer un service S3 sur diverses machines éloignées.
|
|
||||||
\end{center}
|
|
||||||
\column{0.5 \linewidth}
|
|
||||||
\begin{center}
|
|
||||||
\includegraphics[width=4cm]{garage-logo.png}\linebreak
|
|
||||||
|
|
||||||
\texttt{https://garagehq.deuxfleurs.fr/}
|
|
||||||
\end{center}
|
|
||||||
\end{columns}
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\subsection{Gestion des zones}
|
|
||||||
\begin{frame}
|
|
||||||
\begin{center}
|
|
||||||
Garage va prendre en compte les zones géographiques au moment de répliquer les données.\linebreak
|
|
||||||
|
|
||||||
\vspace{1cm}
|
|
||||||
\includegraphics[width=13.25cm]{zones.png}
|
|
||||||
\end{center}
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\subsection{Comment ça marche ?}
|
|
||||||
\begin{frame}
|
|
||||||
\begin{columns}
|
|
||||||
\column{0.5 \linewidth}
|
|
||||||
\input{schéma europe}
|
|
||||||
\column{0.5 \linewidth}
|
|
||||||
\begin{center}
|
|
||||||
Chaque objet est dupliqué sur plusieurs zones différentes.\linebreak
|
|
||||||
|
|
||||||
\onslide<5->{Lorsqu'un nouvel hébergeur rejoint le réseau, la charge se voit équilibrée.}\linebreak
|
|
||||||
|
|
||||||
\onslide<12->{Si une zone devient indisponible, les autres continuent d'assurer le service.}\linebreak
|
|
||||||
\end{center}
|
|
||||||
\end{columns}
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\subsection{Financement}
|
|
||||||
\begin{frame}
|
|
||||||
\begin{center}
|
|
||||||
Dans le cadre du programme \textit{Horizon 2021} de l'Union Européenne, nous avons reçu une subvention de la part de l'initiative NGI Pointer\footnote[frame]{Next Generation Internet Program for Open Internet Renovation}.\linebreak
|
|
||||||
|
|
||||||
\includegraphics[width=3cm]{drapeau_européen.png}\hspace{1cm}
|
|
||||||
\includegraphics[width=3cm]{NGI.png}\linebreak
|
|
||||||
|
|
||||||
Nous avons ainsi pu financer le développement de Garage pendant 1 an.
|
|
||||||
\end{center}
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\subsection{Licence}
|
|
||||||
\begin{frame}
|
|
||||||
\begin{center}
|
|
||||||
De par nos valeurs, nous avons attribué la licence AGPL version 3 à Garage, notamment afin qu'il reste parmi les biens communs.\linebreak
|
|
||||||
|
|
||||||
\vspace{0.5cm}
|
|
||||||
\includegraphics[width=5cm]{agpl-v3-logo.png}\linebreak
|
|
||||||
\end{center}
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\subsection{Langage utilisé}
|
|
||||||
\begin{frame}
|
|
||||||
\begin{center}
|
|
||||||
Nous avons décidé d'écrire Garage à l'aide du langage Rust, afin d'obtenir une compilation vers des binaires natifs et efficaces.\linebreak
|
|
||||||
|
|
||||||
\includegraphics[width=3.5cm]{rust-logo.png}\linebreak
|
|
||||||
|
|
||||||
Ce choix permet également de bénéficier des avantages reconnus de Rust en termes de sécurité.
|
|
||||||
\end{center}
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\subsection{Matériel requis}
|
|
||||||
\begin{frame}
|
|
||||||
\begin{center}
|
|
||||||
Garage peut ainsi être performant sur des machines limitées. Les prérequis sont minimes : n'importe quelle machine avec un processeur qui a moins d'une décennie, 1~gigaoctet de mémoire vive, et 16~gigaoctets de stockage suffit.\linebreak
|
|
||||||
|
|
||||||
\vspace{1cm}
|
|
||||||
|
|
||||||
Cet aspect est déterminant : il permet en effet d'héberger sur du matériel acheté d'occasion, pour réduire l'impact écologique de nos infrastructures.
|
|
||||||
\end{center}
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\subsection{Performances}
|
|
||||||
\begin{frame}
|
|
||||||
\begin{center}
|
|
||||||
\includegraphics[width=13.25cm]{rpc-amplification.png}
|
|
||||||
\end{center}
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\begin{frame}
|
|
||||||
\begin{center}
|
|
||||||
\includegraphics[width=11cm]{rpc-complexity.png}
|
|
||||||
\end{center}
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\subsection{Services}
|
|
||||||
\begin{frame}
|
|
||||||
\begin{center}
|
|
||||||
Puisqu'il suit le standard S3, beaucoup de services populaires sont par conséquence compatibles avec Garage :\linebreak
|
|
||||||
|
|
||||||
\begin{columns}
|
|
||||||
\column{0.2 \linewidth}
|
|
||||||
\begin{center}
|
|
||||||
\includegraphics[width=2.5cm]{nextcloud-logo.png}
|
|
||||||
\end{center}
|
|
||||||
\column{0.2 \linewidth}
|
|
||||||
\begin{center}
|
|
||||||
\includegraphics[width=2.5cm]{peertube-logo.png}
|
|
||||||
\end{center}
|
|
||||||
\column{0.2 \linewidth}
|
|
||||||
\begin{center}
|
|
||||||
\includegraphics[width=2.5cm]{matrix-logo.png}
|
|
||||||
\end{center}
|
|
||||||
\column{0.2 \linewidth}
|
|
||||||
\begin{center}
|
|
||||||
\includegraphics[width=2.5cm]{mastodon-logo.png}
|
|
||||||
\end{center}
|
|
||||||
\end{columns}
|
|
||||||
~\linebreak
|
|
||||||
|
|
||||||
Et comme souvent avec S3, on peut assimiler un bucket à un site, et utiliser le serveur pour héberger des sites web statiques.
|
|
||||||
\end{center}
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\section{Intégration chez Deuxfleurs}
|
|
||||||
\subsection{Matériel}
|
|
||||||
\begin{frame}
|
|
||||||
\begin{center}
|
|
||||||
\includegraphics[width=13cm]{neptune.jpg}\linebreak
|
|
||||||
|
|
||||||
En pratique, nos serveurs ne sont effectivement que des machines achetées d'occasion (très souvent des anciens ordinateurs destinés à la bureautique en entreprise).
|
|
||||||
\end{center}
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\subsection{Environnement logiciel}
|
|
||||||
\begin{frame}
|
|
||||||
\begin{center}
|
|
||||||
Pour faciliter la reproduction d'un environnement connu, NixOS est installé sur nos machines.\linebreak
|
|
||||||
|
|
||||||
\vspace{1cm}
|
|
||||||
Pour s’accommoder des réseaux qu'on trouve derrière des routeurs pour particuliers, on s'aide de notre logiciel Diplonat\footnote[frame]{\texttt{https://git.deuxfleurs.fr/Deuxfleurs/diplonat}}.
|
|
||||||
\end{center}
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\section{Au-delà...}
|
|
||||||
\subsection{... de Deuxfleurs}
|
|
||||||
\begin{frame}
|
|
||||||
\begin{center}
|
|
||||||
\includegraphics[width=10cm]{tedomum.png}
|
|
||||||
\end{center}
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\subsection{... de Garage}
|
|
||||||
\begin{frame}
|
|
||||||
\begin{center}
|
|
||||||
Nous avons récemment lancé le développement d'Aérogramme\footnote[frame]{\texttt{https://git.deuxfleurs.fr/Deuxfleurs/aerogramme}}.\linebreak
|
|
||||||
|
|
||||||
\vspace{1cm}
|
|
||||||
Il s'agit d'un serveur de stockage de courriels chiffrés.\linebreak
|
|
||||||
|
|
||||||
\vspace{1cm}
|
|
||||||
Il est conçu pour pouvoir travailler avec Garage.
|
|
||||||
\end{center}
|
|
||||||
\end{frame}
|
|
||||||
|
|
||||||
\section{Fin}
|
|
||||||
\subsection{Contacts}
|
|
||||||
\begin{frame}
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}
|
|
||||||
\node (ronce) {\includegraphics[width=0.95\textwidth]{ronce.jpg}};
|
|
||||||
\node[white] at (-5.1,3.6) {Intéressé(e) ?};
|
|
||||||
\node[white, align=center] at (4.2,-2.6) {Contactez-nous !\\\texttt{coucou@deuxfleurs.fr}\\\texttt{\#forum:deuxfleurs.fr}};
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
\end{frame}
|
|
||||||
\end{document}
|
|
||||||
|
|
Before Width: | Height: | Size: 1.4 MiB |
Before Width: | Height: | Size: 124 KiB |
Before Width: | Height: | Size: 194 KiB |
Before Width: | Height: | Size: 6.5 KiB |
|
@ -1,52 +0,0 @@
|
||||||
\begin{tikzpicture}
|
|
||||||
\node (carte) {\includegraphics[width=\textwidth]{carte-Europe.pdf}};
|
|
||||||
|
|
||||||
% \personnage{position X}{position Y}{facteur d'échelle}
|
|
||||||
\newcommand{\personnage}[4]
|
|
||||||
{
|
|
||||||
\fill[#4] ({#1-(0.4 * #3)},{#2-(0.9 * #3)}) .. controls ({#1-(0.4 * #3)},#2) and ({#1+(0.4 * #3)},#2) .. ({#1+(0.4 * #3)},{#2-(0.9 * #3)}) -- ({#1-(0.4 * #3)},{#2-(0.9 * #3)});
|
|
||||||
\fill[#4] (#1,#2) circle ({0.25 * #3});
|
|
||||||
}
|
|
||||||
|
|
||||||
\onslide<1-11>{\personnage{-2.25}{-0.75}{0.75}{green}}
|
|
||||||
\onslide<1-11>{\draw (-1.9,-1.6) rectangle ++(1,1.2);}
|
|
||||||
\onslide<2-11>{\draw[fill=green] (-1.8,-1.525) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 1};}
|
|
||||||
\onslide<4-5>{\draw[fill=red] (-1.8,-1.15) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 3};}
|
|
||||||
\onslide<7-11>{\draw[fill=yellow] (-1.8,-1.15) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 4};}
|
|
||||||
\onslide<9-11>{\draw[fill=red] (-1.8,-0.775) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 6};}
|
|
||||||
\onslide<3-11>{\draw[fill=blue] (-1.35,-1.525) rectangle ++(0.35,0.3) node[pos=0.5, white] {\tiny 2};}
|
|
||||||
\onslide<8-11>{\draw[fill=blue] (-1.35,-1.15) rectangle ++(0.35,0.3) node[pos=0.5, white] {\tiny 5};}
|
|
||||||
\onslide<11-11>{\draw[fill=yellow] (-1.35,-0.775) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 8};}
|
|
||||||
|
|
||||||
\personnage{1.65}{1.5}{0.75}{blue}
|
|
||||||
\draw (0.3,0.7) rectangle ++(1,1.2);
|
|
||||||
\onslide<2->{\draw[fill=green] (0.4,0.775) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 1};}
|
|
||||||
\onslide<4->{\draw[fill=red] (0.4,1.15) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 3};}
|
|
||||||
\onslide<10->{\draw[fill=green] (0.4,1.525) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 7};}
|
|
||||||
\onslide<3->{\draw[fill=blue] (0.85,0.775) rectangle ++(0.35,0.3) node[pos=0.5, white] {\tiny 2};}
|
|
||||||
\onslide<9->{\draw[fill=red] (0.85,1.15) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 6};}
|
|
||||||
\onslide<11->{\draw[fill=yellow] (0.85,1.525) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 8};}
|
|
||||||
|
|
||||||
\personnage{1.85}{-2.3}{0.75}{red}
|
|
||||||
\draw (0.5,-3.15) rectangle ++(1,1.2);
|
|
||||||
\onslide<2->{\draw[fill=green] (0.6,-3.075) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 1};}
|
|
||||||
\onslide<4-5>{\draw[fill=red] (0.6,-2.7) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 3};}
|
|
||||||
\onslide<7->{\draw[fill=yellow] (0.6,-2.7) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 4};}
|
|
||||||
\onslide<9->{\draw[fill=red] (0.6,-2.325) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 6};}
|
|
||||||
\onslide<3-5>{\draw[fill=blue] (1.05,-3.075) rectangle ++(0.35,0.3) node[pos=0.5, white] {\tiny 2};}
|
|
||||||
\onslide<6->{\draw[fill=red] (1.05,-3.075) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 3};}
|
|
||||||
\onslide<8->{\draw[fill=blue] (1.05,-2.7) rectangle ++(0.35,0.3) node[pos=0.5, white] {\tiny 5};}
|
|
||||||
\onslide<10->{\draw[fill=green] (1.05,-2.325) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 7};}
|
|
||||||
|
|
||||||
\onslide<5->{\personnage{1.05}{-0.15}{0.75}{yellow}}
|
|
||||||
\onslide<5->{\draw (-0.35,-1) rectangle ++(1,1.2);}
|
|
||||||
\onslide<6->{\draw[fill=blue] (-0.25,-0.925) rectangle ++(0.35,0.3) node[pos=0.5, white] {\tiny 2};}
|
|
||||||
\onslide<7->{\draw[fill=yellow] (-0.25,-0.55) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 4};}
|
|
||||||
\onslide<10->{\draw[fill=green] (-0.25,-0.175) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 7};}
|
|
||||||
\onslide<6->{\draw[fill=red] (0.2,-0.925) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 3};}
|
|
||||||
\onslide<8->{\draw[fill=blue] (0.2,-0.55) rectangle ++(0.35,0.3) node[pos=0.5,white] {\tiny 5};}
|
|
||||||
\onslide<11->{\draw[fill=yellow] (0.2,-0.175) rectangle ++(0.35,0.3) node[pos=0.5] {\tiny 8};}
|
|
||||||
|
|
||||||
\onslide<12->{\draw[line width=0.25cm] (-2.15,-0.5) -- ++(1,-1);}
|
|
||||||
\onslide<12->{\draw[line width=0.25cm] (-2.15,-1.5) -- ++(1,1);}
|
|
||||||
\end{tikzpicture}
|
|
Before Width: | Height: | Size: 236 KiB |
Before Width: | Height: | Size: 97 KiB |
108
flake.lock
|
@ -1,108 +0,0 @@
|
||||||
{
|
|
||||||
"nodes": {
|
|
||||||
"cargo2nix": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-compat": "flake-compat",
|
|
||||||
"flake-utils": "flake-utils",
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"rust-overlay": "rust-overlay"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1666087781,
|
|
||||||
"narHash": "sha256-trKVdjMZ8mNkGfLcY5LsJJGtdV3xJDZnMVrkFjErlcs=",
|
|
||||||
"owner": "Alexis211",
|
|
||||||
"repo": "cargo2nix",
|
|
||||||
"rev": "a7a61179b66054904ef6a195d8da736eaaa06c36",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "Alexis211",
|
|
||||||
"repo": "cargo2nix",
|
|
||||||
"rev": "a7a61179b66054904ef6a195d8da736eaaa06c36",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-compat": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1650374568,
|
|
||||||
"narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=",
|
|
||||||
"owner": "edolstra",
|
|
||||||
"repo": "flake-compat",
|
|
||||||
"rev": "b4a34015c698c7793d592d66adbab377907a2be8",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "edolstra",
|
|
||||||
"repo": "flake-compat",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-utils": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1659877975,
|
|
||||||
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1665657542,
|
|
||||||
"narHash": "sha256-mojxNyzbvmp8NtVtxqiHGhRfjCALLfk9i/Uup68Y5q8=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "a3073c49bc0163fea6a121c276f526837672b555",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "a3073c49bc0163fea6a121c276f526837672b555",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
|
||||||
"inputs": {
|
|
||||||
"cargo2nix": "cargo2nix",
|
|
||||||
"nixpkgs": "nixpkgs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rust-overlay": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-utils": [
|
|
||||||
"cargo2nix",
|
|
||||||
"flake-utils"
|
|
||||||
],
|
|
||||||
"nixpkgs": [
|
|
||||||
"cargo2nix",
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1664247556,
|
|
||||||
"narHash": "sha256-J4vazHU3609ekn7dr+3wfqPo5WGlZVAgV7jfux352L0=",
|
|
||||||
"owner": "oxalica",
|
|
||||||
"repo": "rust-overlay",
|
|
||||||
"rev": "524db9c9ea7bc7743bb74cdd45b6d46ea3fcc2ab",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "oxalica",
|
|
||||||
"repo": "rust-overlay",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": "root",
|
|
||||||
"version": 7
|
|
||||||
}
|
|
28
flake.nix
|
@ -1,28 +0,0 @@
|
||||||
{
|
|
||||||
description = "Garage, an S3-compatible distributed object store for self-hosted deployments";
|
|
||||||
|
|
||||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/a3073c49bc0163fea6a121c276f526837672b555";
|
|
||||||
inputs.cargo2nix = {
|
|
||||||
# As of 2022-10-18: two small patches over unstable branch, one for clippy and one to fix feature detection
|
|
||||||
url = "github:Alexis211/cargo2nix/a7a61179b66054904ef6a195d8da736eaaa06c36";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
|
|
||||||
outputs = { self, nixpkgs, cargo2nix }: let
|
|
||||||
git_version = self.lastModifiedDate;
|
|
||||||
compile = import ./nix/compile.nix;
|
|
||||||
forAllSystems = nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
packages = forAllSystems (system: {
|
|
||||||
default = (compile {
|
|
||||||
inherit system git_version;
|
|
||||||
pkgsSrc = nixpkgs;
|
|
||||||
cargo2nixOverlay = cargo2nix.overlays.default;
|
|
||||||
release = true;
|
|
||||||
}).workspace.garage {
|
|
||||||
compileMode = "build";
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -3,20 +3,22 @@ rec {
|
||||||
* Fixed dependencies
|
* Fixed dependencies
|
||||||
*/
|
*/
|
||||||
pkgsSrc = fetchTarball {
|
pkgsSrc = fetchTarball {
|
||||||
# As of 2022-10-13
|
# As of 2021-10-04
|
||||||
url = "https://github.com/NixOS/nixpkgs/archive/a3073c49bc0163fea6a121c276f526837672b555.zip";
|
url ="https://github.com/NixOS/nixpkgs/archive/b27d18a412b071f5d7991d1648cfe78ee7afe68a.tar.gz";
|
||||||
sha256 = "1bz632psfbpmicyzjb8b4265y50shylccvfm6ry6mgnv5hvz324s";
|
sha256 = "1xy9zpypqfxs5gcq5dcla4bfkhxmh5nzn9dyqkr03lqycm9wg5cr";
|
||||||
};
|
};
|
||||||
cargo2nixSrc = fetchGit {
|
cargo2nixSrc = fetchGit {
|
||||||
# As of 2022-10-18: two small patches over unstable branch, one for clippy and one to fix feature detection
|
# As of 2022-03-17
|
||||||
url = "https://github.com/Alexis211/cargo2nix";
|
url = "https://github.com/superboum/cargo2nix";
|
||||||
ref = "custom_unstable";
|
ref = "main";
|
||||||
rev = "a7a61179b66054904ef6a195d8da736eaaa06c36";
|
rev = "bcbf3ba99e9e01a61eb83a24624419c2dd9dec64";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Shared objects
|
* Shared objects
|
||||||
*/
|
*/
|
||||||
cargo2nix = import cargo2nixSrc;
|
cargo2nix = import cargo2nixSrc;
|
||||||
cargo2nixOverlay = cargo2nix.overlays.default;
|
cargo2nixOverlay = import "${cargo2nixSrc}/overlay";
|
||||||
}
|
}
|
||||||
|
|
227
nix/compile.nix
|
@ -1,227 +0,0 @@
|
||||||
{
|
|
||||||
system,
|
|
||||||
target ? null,
|
|
||||||
pkgsSrc,
|
|
||||||
cargo2nixOverlay,
|
|
||||||
compiler ? "rustc",
|
|
||||||
release ? false,
|
|
||||||
git_version ? null,
|
|
||||||
features ? null,
|
|
||||||
}:
|
|
||||||
|
|
||||||
let
|
|
||||||
log = v: builtins.trace v v;
|
|
||||||
|
|
||||||
pkgs =
|
|
||||||
if target != null then
|
|
||||||
import pkgsSrc {
|
|
||||||
inherit system;
|
|
||||||
crossSystem = {
|
|
||||||
config = target;
|
|
||||||
isStatic = true;
|
|
||||||
};
|
|
||||||
overlays = [ cargo2nixOverlay ];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
import pkgsSrc {
|
|
||||||
inherit system;
|
|
||||||
overlays = [ cargo2nixOverlay ];
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
Cargo2nix is built for rustOverlay which installs Rust from Mozilla releases.
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
toolchainOptions =
|
|
||||||
if target == null || 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
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
buildEnv = (drv: {
|
|
||||||
rustc = drv.setBuildEnv;
|
|
||||||
clippy = ''
|
|
||||||
${drv.setBuildEnv or "" }
|
|
||||||
echo
|
|
||||||
echo --- BUILDING WITH CLIPPY ---
|
|
||||||
echo
|
|
||||||
|
|
||||||
export NIX_RUST_BUILD_FLAGS="''${NIX_RUST_BUILD_FLAGS} --deny warnings"
|
|
||||||
export RUSTC="''${CLIPPY_DRIVER}"
|
|
||||||
'';
|
|
||||||
}.${compiler});
|
|
||||||
|
|
||||||
/*
|
|
||||||
Cargo2nix provides many overrides by default, you can take inspiration from them:
|
|
||||||
https://github.com/cargo2nix/cargo2nix/blob/master/overlay/overrides.nix
|
|
||||||
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
packageOverrides = pkgs: pkgs.rustBuilder.overrides.all ++ [
|
|
||||||
/*
|
|
||||||
[1] We add some logic to compile our crates with clippy, it provides us many additional lints
|
|
||||||
|
|
||||||
[2] We need to alter Nix hardening to make static binaries: PIE,
|
|
||||||
Position Independent Executables seems to be supported only on amd64. Having
|
|
||||||
this flag set either 1. make our executables crash or 2. compile as dynamic on some platforms.
|
|
||||||
Here, we deactivate it. Later (find `codegenOpts`), we reactivate it for supported targets
|
|
||||||
(only amd64 curently) through the `-static-pie` flag.
|
|
||||||
PIE is a feature used by ASLR, which helps mitigate security issues.
|
|
||||||
Learn more about Nix Hardening at: https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/cc-wrapper/add-hardening.sh
|
|
||||||
|
|
||||||
[3] We want to inject the git version while keeping the build deterministic.
|
|
||||||
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 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:
|
|
||||||
(if git_version != null then {
|
|
||||||
/* [3] */ preConfigure = ''
|
|
||||||
${drv.preConfigure or ""}
|
|
||||||
export GIT_VERSION="${git_version}"
|
|
||||||
'';
|
|
||||||
} else {})
|
|
||||||
//
|
|
||||||
{
|
|
||||||
/* [1] */ setBuildEnv = (buildEnv drv);
|
|
||||||
/* [2] */ hardeningDisable = [ "pie" ];
|
|
||||||
};
|
|
||||||
})
|
|
||||||
|
|
||||||
(pkgs.rustBuilder.rustLib.makeOverride {
|
|
||||||
name = "garage_rpc";
|
|
||||||
overrideAttrs = drv: { /* [1] */ setBuildEnv = (buildEnv drv); };
|
|
||||||
})
|
|
||||||
|
|
||||||
(pkgs.rustBuilder.rustLib.makeOverride {
|
|
||||||
name = "garage_db";
|
|
||||||
overrideAttrs = drv: { /* [1] */ setBuildEnv = (buildEnv drv); };
|
|
||||||
})
|
|
||||||
|
|
||||||
(pkgs.rustBuilder.rustLib.makeOverride {
|
|
||||||
name = "garage_util";
|
|
||||||
overrideAttrs = drv: { /* [1] */ setBuildEnv = (buildEnv drv); };
|
|
||||||
})
|
|
||||||
|
|
||||||
(pkgs.rustBuilder.rustLib.makeOverride {
|
|
||||||
name = "garage_table";
|
|
||||||
overrideAttrs = drv: { /* [1] */ setBuildEnv = (buildEnv drv); };
|
|
||||||
})
|
|
||||||
|
|
||||||
(pkgs.rustBuilder.rustLib.makeOverride {
|
|
||||||
name = "garage_block";
|
|
||||||
overrideAttrs = drv: { /* [1] */ setBuildEnv = (buildEnv drv); };
|
|
||||||
})
|
|
||||||
|
|
||||||
(pkgs.rustBuilder.rustLib.makeOverride {
|
|
||||||
name = "garage_model";
|
|
||||||
overrideAttrs = drv: { /* [1] */ setBuildEnv = (buildEnv drv); };
|
|
||||||
})
|
|
||||||
|
|
||||||
(pkgs.rustBuilder.rustLib.makeOverride {
|
|
||||||
name = "garage_api";
|
|
||||||
overrideAttrs = drv: { /* [1] */ setBuildEnv = (buildEnv drv); };
|
|
||||||
})
|
|
||||||
|
|
||||||
(pkgs.rustBuilder.rustLib.makeOverride {
|
|
||||||
name = "garage_web";
|
|
||||||
overrideAttrs = drv: { /* [1] */ setBuildEnv = (buildEnv drv); };
|
|
||||||
})
|
|
||||||
|
|
||||||
(pkgs.rustBuilder.rustLib.makeOverride {
|
|
||||||
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;
|
|
||||||
|
|
||||||
/*
|
|
||||||
We compile fully static binaries with musl to simplify deployment on most systems.
|
|
||||||
When possible, we reactivate PIE hardening (see above).
|
|
||||||
|
|
||||||
Also, if you set the RUSTFLAGS environment variable, the following parameters will
|
|
||||||
be ignored.
|
|
||||||
|
|
||||||
For more information on static builds, please refer to Rust's RFC 1721.
|
|
||||||
https://rust-lang.github.io/rfcs/1721-crt-static.html#specifying-dynamicstatic-c-runtime-linkage
|
|
||||||
*/
|
|
||||||
|
|
||||||
codegenOpts = {
|
|
||||||
"armv6l-unknown-linux-musleabihf" = [ "target-feature=+crt-static" "link-arg=-static" ]; /* compile as dynamic with static-pie */
|
|
||||||
"aarch64-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static" ]; /* segfault with static-pie */
|
|
||||||
"i686-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static" ]; /* segfault with static-pie */
|
|
||||||
"x86_64-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static-pie" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
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;
|
|
||||||
|
|
||||||
in
|
|
||||||
pkgs.rustBuilder.makePackageSet ({
|
|
||||||
inherit release packageFun packageOverrides codegenOpts rootFeatures;
|
|
||||||
target = rustTarget;
|
|
||||||
} // toolchainOptions)
|
|
|
@ -1,23 +0,0 @@
|
||||||
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;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,9 +1,5 @@
|
||||||
substituters = https://cache.nixos.org https://nix.web.deuxfleurs.fr
|
substituters = https://cache.nixos.org https://nix.web.deuxfleurs.fr
|
||||||
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= nix.web.deuxfleurs.fr:eTGL6kvaQn6cDR/F9lDYUIP9nCVR/kkshYfLDJf1yKs=
|
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= nix.web.deuxfleurs.fr:eTGL6kvaQn6cDR/F9lDYUIP9nCVR/kkshYfLDJf1yKs=
|
||||||
max-jobs = auto
|
max-jobs = auto
|
||||||
cores = 0
|
cores = 4
|
||||||
log-lines = 200
|
log-lines = 200
|
||||||
filter-syscalls = false
|
|
||||||
sandbox = false
|
|
||||||
keep-outputs = true
|
|
||||||
keep-derivations = true
|
|
||||||
|
|
|
@ -6,24 +6,19 @@ with import ./common.nix;
|
||||||
|
|
||||||
let
|
let
|
||||||
platforms = [
|
platforms = [
|
||||||
#"x86_64-unknown-linux-musl"
|
"x86_64-unknown-linux-musl"
|
||||||
"i686-unknown-linux-musl"
|
"i686-unknown-linux-musl"
|
||||||
#"aarch64-unknown-linux-musl"
|
"aarch64-unknown-linux-musl"
|
||||||
"armv6l-unknown-linux-musleabihf"
|
"armv6l-unknown-linux-musleabihf"
|
||||||
];
|
];
|
||||||
pkgsList = builtins.map (target: import pkgsSrc {
|
pkgsList = builtins.map (target: import pkgsSrc {
|
||||||
inherit system;
|
inherit system;
|
||||||
crossSystem = {
|
crossSystem = { config = target; };
|
||||||
config = target;
|
|
||||||
isStatic = true;
|
|
||||||
};
|
|
||||||
overlays = [ cargo2nixOverlay ];
|
|
||||||
}) platforms;
|
}) platforms;
|
||||||
pkgsHost = import pkgsSrc {};
|
pkgsHost = import pkgsSrc {};
|
||||||
lib = pkgsHost.lib;
|
lib = pkgsHost.lib;
|
||||||
kaniko = (import ./kaniko.nix) pkgsHost;
|
kaniko = (import ./kaniko.nix) pkgsHost;
|
||||||
winscp = (import ./winscp.nix) pkgsHost;
|
winscp = (import ./winscp.nix) pkgsHost;
|
||||||
manifestTool = (import ./manifest-tool.nix) pkgsHost;
|
|
||||||
in
|
in
|
||||||
lib.flatten (builtins.map (pkgs: [
|
lib.flatten (builtins.map (pkgs: [
|
||||||
pkgs.rustPlatform.rust.rustc
|
pkgs.rustPlatform.rust.rustc
|
||||||
|
@ -32,6 +27,5 @@ in
|
||||||
]) pkgsList) ++ [
|
]) pkgsList) ++ [
|
||||||
kaniko
|
kaniko
|
||||||
winscp
|
winscp
|
||||||
manifestTool
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ PATH="${GARAGE_DEBUG}:${GARAGE_RELEASE}:${NIX_RELEASE}:$PATH"
|
||||||
FANCYCOLORS=("41m" "42m" "44m" "45m" "100m" "104m")
|
FANCYCOLORS=("41m" "42m" "44m" "45m" "100m" "104m")
|
||||||
|
|
||||||
export RUST_BACKTRACE=1
|
export RUST_BACKTRACE=1
|
||||||
export RUST_LOG=garage=info,garage_api=debug,netapp=trace
|
export RUST_LOG=garage=info,garage_api=debug
|
||||||
MAIN_LABEL="\e[${FANCYCOLORS[0]}[main]\e[49m"
|
MAIN_LABEL="\e[${FANCYCOLORS[0]}[main]\e[49m"
|
||||||
|
|
||||||
WHICH_GARAGE=$(which garage || exit 1)
|
WHICH_GARAGE=$(which garage || exit 1)
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
# Garage helm3 chart
|
|
||||||
|
|
||||||
Documentation is located [here](/doc/book/cookbook/kubernetes.md).
|
|
|
@ -1,23 +0,0 @@
|
||||||
# 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/
|
|
|
@ -1,24 +0,0 @@
|
||||||
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.2.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"
|
|