mirror of
https://github.com/deuxfleurs-org/garage.git
synced 2026-09-07 04:25:56 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 33d50666b5 |
Generated
+14
-13
@@ -1513,7 +1513,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "garage"
|
name = "garage"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert-json-diff",
|
"assert-json-diff",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -1581,7 +1581,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "garage_api_admin"
|
name = "garage_api_admin"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argon2",
|
"argon2",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -1613,7 +1613,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "garage_api_common"
|
name = "garage_api_common"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -1649,7 +1649,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "garage_api_k2v"
|
name = "garage_api_k2v"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"futures",
|
"futures",
|
||||||
@@ -1672,7 +1672,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "garage_api_s3"
|
name = "garage_api_s3"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
"async-compression",
|
"async-compression",
|
||||||
@@ -1717,7 +1717,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "garage_block"
|
name = "garage_block"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"async-compression",
|
"async-compression",
|
||||||
@@ -1741,7 +1741,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "garage_db"
|
name = "garage_db"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fjall",
|
"fjall",
|
||||||
"heed",
|
"heed",
|
||||||
@@ -1756,7 +1756,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "garage_model"
|
name = "garage_model"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arbitrary",
|
"arbitrary",
|
||||||
"argon2",
|
"argon2",
|
||||||
@@ -1784,7 +1784,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "garage_net"
|
name = "garage_net"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -1810,7 +1810,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "garage_rpc"
|
name = "garage_rpc"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -1843,7 +1843,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "garage_table"
|
name = "garage_table"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -1864,7 +1864,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "garage_util"
|
name = "garage_util"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arbitrary",
|
"arbitrary",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
@@ -1890,6 +1890,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2 0.10.9",
|
"sha2 0.10.9",
|
||||||
|
"subtle",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
@@ -1899,7 +1900,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "garage_web"
|
name = "garage_web"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"garage_api_common",
|
"garage_api_common",
|
||||||
"garage_api_s3",
|
"garage_api_s3",
|
||||||
|
|||||||
+13
-12
@@ -25,18 +25,18 @@ default-members = ["src/garage"]
|
|||||||
|
|
||||||
# Internal Garage crates
|
# Internal Garage crates
|
||||||
format_table = { version = "0.1.1", path = "src/format-table" }
|
format_table = { version = "0.1.1", path = "src/format-table" }
|
||||||
garage_api_common = { version = "2.4.0", path = "src/api/common" }
|
garage_api_common = { version = "2.3.0", path = "src/api/common" }
|
||||||
garage_api_admin = { version = "2.4.0", path = "src/api/admin" }
|
garage_api_admin = { version = "2.3.0", path = "src/api/admin" }
|
||||||
garage_api_s3 = { version = "2.4.0", path = "src/api/s3" }
|
garage_api_s3 = { version = "2.3.0", path = "src/api/s3" }
|
||||||
garage_api_k2v = { version = "2.4.0", path = "src/api/k2v" }
|
garage_api_k2v = { version = "2.3.0", path = "src/api/k2v" }
|
||||||
garage_block = { version = "2.4.0", path = "src/block" }
|
garage_block = { version = "2.3.0", path = "src/block" }
|
||||||
garage_db = { version = "2.4.0", path = "src/db", default-features = false }
|
garage_db = { version = "2.3.0", path = "src/db", default-features = false }
|
||||||
garage_model = { version = "2.4.0", path = "src/model", default-features = false }
|
garage_model = { version = "2.3.0", path = "src/model", default-features = false }
|
||||||
garage_net = { version = "2.4.0", path = "src/net" }
|
garage_net = { version = "2.3.0", path = "src/net" }
|
||||||
garage_rpc = { version = "2.4.0", path = "src/rpc" }
|
garage_rpc = { version = "2.3.0", path = "src/rpc" }
|
||||||
garage_table = { version = "2.4.0", path = "src/table" }
|
garage_table = { version = "2.3.0", path = "src/table" }
|
||||||
garage_util = { version = "2.4.0", path = "src/util" }
|
garage_util = { version = "2.3.0", path = "src/util" }
|
||||||
garage_web = { version = "2.4.0", path = "src/web" }
|
garage_web = { version = "2.3.0", path = "src/web" }
|
||||||
k2v-client = { version = "0.0.4", path = "src/k2v-client" }
|
k2v-client = { version = "0.0.4", path = "src/k2v-client" }
|
||||||
|
|
||||||
# External crates from crates.io
|
# External crates from crates.io
|
||||||
@@ -76,6 +76,7 @@ pnet_datalink = "0.35"
|
|||||||
rand = "0.9"
|
rand = "0.9"
|
||||||
sha1 = "0.10"
|
sha1 = "0.10"
|
||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
|
subtle = "2.6.1"
|
||||||
timeago = { version = "0.5", default-features = false }
|
timeago = { version = "0.5", default-features = false }
|
||||||
xxhash-rust = { version = "0.8", default-features = false, features = ["xxh3"] }
|
xxhash-rust = { version = "0.8", default-features = false, features = ["xxh3"] }
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
"name": "AGPL-3.0",
|
"name": "AGPL-3.0",
|
||||||
"identifier": "AGPL-3.0"
|
"identifier": "AGPL-3.0"
|
||||||
},
|
},
|
||||||
"version": "v2.4.0"
|
"version": "v2.3.0"
|
||||||
},
|
},
|
||||||
"servers": [
|
"servers": [
|
||||||
{
|
{
|
||||||
@@ -3321,35 +3321,10 @@
|
|||||||
"dbEngine"
|
"dbEngine"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"addr": {
|
|
||||||
"type": [
|
|
||||||
"string",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"description": "Socket address used by other nodes to connect to this node for RPC"
|
|
||||||
},
|
|
||||||
"dataPartition": {
|
|
||||||
"oneOf": [
|
|
||||||
{
|
|
||||||
"type": "null"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/FreeSpaceResp",
|
|
||||||
"description": "Total and available space on the disk partition(s) containing the data\ndirectory(ies)"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"dbEngine": {
|
"dbEngine": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "database engine used for metadata"
|
"description": "database engine used for metadata"
|
||||||
},
|
},
|
||||||
"draining": {
|
|
||||||
"type": [
|
|
||||||
"boolean",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"description": "Whether this node is part of an older layout version and is draining data."
|
|
||||||
},
|
|
||||||
"garageFeatures": {
|
"garageFeatures": {
|
||||||
"type": [
|
"type": [
|
||||||
"array",
|
"array",
|
||||||
@@ -3371,38 +3346,9 @@
|
|||||||
],
|
],
|
||||||
"description": "hostname of this node"
|
"description": "hostname of this node"
|
||||||
},
|
},
|
||||||
"isUp": {
|
|
||||||
"type": [
|
|
||||||
"boolean",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"description": "Whether this node is connected in the cluster"
|
|
||||||
},
|
|
||||||
"metadataPartition": {
|
|
||||||
"oneOf": [
|
|
||||||
{
|
|
||||||
"type": "null"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/FreeSpaceResp",
|
|
||||||
"description": "Total and available space on the disk partition containing the\nmetadata directory"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"nodeId": {
|
"nodeId": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"role": {
|
|
||||||
"oneOf": [
|
|
||||||
{
|
|
||||||
"type": "null"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/NodeAssignedRole",
|
|
||||||
"description": "Role assigned to this node in the current cluster layout"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"rustVersion": {
|
"rustVersion": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "rustc version with which this garage release was compiled"
|
"description": "rustc version with which this garage release was compiled"
|
||||||
@@ -3738,35 +3684,10 @@
|
|||||||
"dbEngine"
|
"dbEngine"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"addr": {
|
|
||||||
"type": [
|
|
||||||
"string",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"description": "Socket address used by other nodes to connect to this node for RPC"
|
|
||||||
},
|
|
||||||
"dataPartition": {
|
|
||||||
"oneOf": [
|
|
||||||
{
|
|
||||||
"type": "null"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/FreeSpaceResp",
|
|
||||||
"description": "Total and available space on the disk partition(s) containing the data\ndirectory(ies)"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"dbEngine": {
|
"dbEngine": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "database engine used for metadata"
|
"description": "database engine used for metadata"
|
||||||
},
|
},
|
||||||
"draining": {
|
|
||||||
"type": [
|
|
||||||
"boolean",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"description": "Whether this node is part of an older layout version and is draining data."
|
|
||||||
},
|
|
||||||
"garageFeatures": {
|
"garageFeatures": {
|
||||||
"type": [
|
"type": [
|
||||||
"array",
|
"array",
|
||||||
@@ -3788,38 +3709,9 @@
|
|||||||
],
|
],
|
||||||
"description": "hostname of this node"
|
"description": "hostname of this node"
|
||||||
},
|
},
|
||||||
"isUp": {
|
|
||||||
"type": [
|
|
||||||
"boolean",
|
|
||||||
"null"
|
|
||||||
],
|
|
||||||
"description": "Whether this node is connected in the cluster"
|
|
||||||
},
|
|
||||||
"metadataPartition": {
|
|
||||||
"oneOf": [
|
|
||||||
{
|
|
||||||
"type": "null"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/FreeSpaceResp",
|
|
||||||
"description": "Total and available space on the disk partition containing the\nmetadata directory"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"nodeId": {
|
"nodeId": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"role": {
|
|
||||||
"oneOf": [
|
|
||||||
{
|
|
||||||
"type": "null"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/NodeAssignedRole",
|
|
||||||
"description": "Role assigned to this node in the current cluster layout"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"rustVersion": {
|
"rustVersion": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "rustc version with which this garage release was compiled"
|
"description": "rustc version with which this garage release was compiled"
|
||||||
|
|||||||
@@ -96,14 +96,14 @@ to store 2 TB of data in total.
|
|||||||
## 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/garage` and is stored on the [Docker Hub](https://hub.docker.com/r/dxflrs/garage/tags?page=1&ordering=last_updated).
|
||||||
We encourage you to use a fixed tag (eg. `v2.4.0`) and not the `latest` tag.
|
We encourage you to use a fixed tag (eg. `v2.3.0`) and not the `latest` tag.
|
||||||
For this example, we will use the latest published version at the time of the writing which is `v2.4.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 `v2.3.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/garage/tags?page=1&ordering=last_updated).
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
```
|
```
|
||||||
docker pull dxflrs/garage:v2.4.0
|
docker pull dxflrs/garage:v2.3.0
|
||||||
```
|
```
|
||||||
|
|
||||||
## Deploying and configuring Garage
|
## Deploying and configuring Garage
|
||||||
@@ -171,7 +171,7 @@ 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:v2.4.0
|
dxflrs/garage:v2.3.0
|
||||||
```
|
```
|
||||||
|
|
||||||
With this command line, Garage should be started automatically at each boot.
|
With this command line, Garage should be started automatically at each boot.
|
||||||
@@ -185,7 +185,7 @@ If you want to use `docker-compose`, you may use the following `docker-compose.y
|
|||||||
version: "3"
|
version: "3"
|
||||||
services:
|
services:
|
||||||
garage:
|
garage:
|
||||||
image: dxflrs/garage:v2.4.0
|
image: dxflrs/garage:v2.3.0
|
||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ your data to be persisted properly.
|
|||||||
|
|
||||||
### Configuring initial access credentials
|
### Configuring initial access credentials
|
||||||
|
|
||||||
Since `v2.n.0`, Garage can automatically create a default access key and a default storage bucket,
|
Since `v2.3.0`, Garage can automatically create a default access key and a default storage bucket,
|
||||||
based on values provided in environment variables.
|
based on values provided in environment variables.
|
||||||
|
|
||||||
To use this feature, export the following environment variables:
|
To use this feature, export the following environment variables:
|
||||||
@@ -157,7 +157,7 @@ docker run \
|
|||||||
-e GARAGE_DEFAULT_ACCESS_KEY \
|
-e GARAGE_DEFAULT_ACCESS_KEY \
|
||||||
-e GARAGE_DEFAULT_SECRET_KEY \
|
-e GARAGE_DEFAULT_SECRET_KEY \
|
||||||
-e GARAGE_DEFAULT_BUCKET \
|
-e GARAGE_DEFAULT_BUCKET \
|
||||||
dxflrs/garage:v2.4.0
|
dxflrs/garage:v2.3.0
|
||||||
/garage server --single-node --default-bucket
|
/garage server --single-node --default-bucket
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -337,7 +337,7 @@ When you first start a cluster without automatic configuration, the output of `g
|
|||||||
```
|
```
|
||||||
==== HEALTHY NODES ====
|
==== HEALTHY NODES ====
|
||||||
ID Hostname Address Tags Zone Capacity DataAvail Version
|
ID Hostname Address Tags Zone Capacity DataAvail Version
|
||||||
563e1ac825ee3323 linuxbox 127.0.0.1:3901 NO ROLE ASSIGNED v2.4.0
|
563e1ac825ee3323 linuxbox 127.0.0.1:3901 NO ROLE ASSIGNED v2.3.0
|
||||||
```
|
```
|
||||||
|
|
||||||
Creating a cluster layout for a Garage deployment means informing Garage of the
|
Creating a cluster layout for a Garage deployment means informing Garage of the
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ apiVersion: v2
|
|||||||
name: garage
|
name: garage
|
||||||
description: S3-compatible object store for small self-hosted geo-distributed deployments
|
description: S3-compatible object store for small self-hosted geo-distributed deployments
|
||||||
type: application
|
type: application
|
||||||
version: 0.9.4
|
version: 0.9.3
|
||||||
appVersion: "v2.4.0"
|
appVersion: "v2.3.0"
|
||||||
home: https://garagehq.deuxfleurs.fr/
|
home: https://garagehq.deuxfleurs.fr/
|
||||||
icon: https://garagehq.deuxfleurs.fr/images/garage-logo.svg
|
icon: https://garagehq.deuxfleurs.fr/images/garage-logo.svg
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# garage
|
# garage
|
||||||
|
|
||||||
  
|
  
|
||||||
|
|
||||||
S3-compatible object store for small self-hosted geo-distributed deployments
|
S3-compatible object store for small self-hosted geo-distributed deployments
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "garage_api_admin"
|
name = "garage_api_admin"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ impl RequestHandler for GetCurrentAdminTokenInfoRequest {
|
|||||||
.admin
|
.admin
|
||||||
.metrics_token
|
.metrics_token
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(|s| s == &self.admin_token)
|
.is_some_and(|s| s.eq_ct(&self.admin_token))
|
||||||
{
|
{
|
||||||
return Ok(GetCurrentAdminTokenInfoResponse(
|
return Ok(GetCurrentAdminTokenInfoResponse(
|
||||||
GetAdminTokenInfoResponse {
|
GetAdminTokenInfoResponse {
|
||||||
@@ -210,7 +210,7 @@ impl RequestHandler for GetCurrentAdminTokenInfoRequest {
|
|||||||
.admin
|
.admin
|
||||||
.admin_token
|
.admin_token
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(|s| s == &self.admin_token)
|
.is_some_and(|s| s.eq_ct(&self.admin_token))
|
||||||
{
|
{
|
||||||
return Ok(GetCurrentAdminTokenInfoResponse(
|
return Ok(GetCurrentAdminTokenInfoResponse(
|
||||||
GetAdminTokenInfoResponse {
|
GetAdminTokenInfoResponse {
|
||||||
|
|||||||
@@ -117,8 +117,14 @@ impl AdminApiServer {
|
|||||||
#[cfg(feature = "metrics")] exporter: PrometheusExporter,
|
#[cfg(feature = "metrics")] exporter: PrometheusExporter,
|
||||||
) -> Arc<Self> {
|
) -> Arc<Self> {
|
||||||
let cfg = &garage.config.admin;
|
let cfg = &garage.config.admin;
|
||||||
let metrics_token = cfg.metrics_token.as_deref().map(hash_bearer_token);
|
let metrics_token = cfg
|
||||||
let admin_token = cfg.admin_token.as_deref().map(hash_bearer_token);
|
.metrics_token
|
||||||
|
.as_ref()
|
||||||
|
.map(|token| hash_bearer_token(token.extract_secret()));
|
||||||
|
let admin_token = cfg
|
||||||
|
.admin_token
|
||||||
|
.as_ref()
|
||||||
|
.map(|token| hash_bearer_token(token.extract_secret()));
|
||||||
let metrics_require_token = cfg.metrics_require_token;
|
let metrics_require_token = cfg.metrics_require_token;
|
||||||
|
|
||||||
let endpoint = garage.system.netapp.endpoint(ADMIN_RPC_PATH.into());
|
let endpoint = garage.system.netapp.endpoint(ADMIN_RPC_PATH.into());
|
||||||
|
|||||||
@@ -876,7 +876,7 @@ impl Modify for SecurityAddon {
|
|||||||
#[derive(OpenApi)]
|
#[derive(OpenApi)]
|
||||||
#[openapi(
|
#[openapi(
|
||||||
info(
|
info(
|
||||||
version = "v2.4.0",
|
version = "v2.3.0",
|
||||||
title = "Garage administration API",
|
title = "Garage administration API",
|
||||||
description = "Administrate your Garage cluster programmatically, including status, layout, keys, buckets, and maintenance tasks.
|
description = "Administrate your Garage cluster programmatically, including status, layout, keys, buckets, and maintenance tasks.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "garage_api_common"
|
name = "garage_api_common"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "garage_api_k2v"
|
name = "garage_api_k2v"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "garage_api_s3"
|
name = "garage_api_s3"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
|
|||||||
+1
-6
@@ -698,14 +698,9 @@ fn body_from_blocks_range(
|
|||||||
// range, as well as their "true offset", which is their actual offset in the complete
|
// range, as well as their "true offset", which is their actual offset in the complete
|
||||||
// file (whereas block.offset designates the offset of the block WITHIN THE PART
|
// file (whereas block.offset designates the offset of the block WITHIN THE PART
|
||||||
// block.part_number, which is not the same in the case of a multipart upload)
|
// block.part_number, which is not the same in the case of a multipart upload)
|
||||||
// A version with no blocks yields no data, so the capacity hint must not index
|
|
||||||
// into an empty slice.
|
|
||||||
let capacity_block_size = all_blocks
|
|
||||||
.first()
|
|
||||||
.map_or(1024, |(_, b)| std::cmp::max(b.size, 1024));
|
|
||||||
let mut blocks: Vec<(VersionBlock, u64)> = Vec::with_capacity(std::cmp::min(
|
let mut blocks: Vec<(VersionBlock, u64)> = Vec::with_capacity(std::cmp::min(
|
||||||
all_blocks.len(),
|
all_blocks.len(),
|
||||||
4 + ((end - begin) / capacity_block_size) as usize,
|
4 + ((end - begin) / std::cmp::max(all_blocks[0].1.size, 1024)) as usize,
|
||||||
));
|
));
|
||||||
let mut block_offset: u64 = 0;
|
let mut block_offset: u64 = 0;
|
||||||
for (_, b) in all_blocks.iter() {
|
for (_, b) in all_blocks.iter() {
|
||||||
|
|||||||
@@ -476,13 +476,7 @@ pub async fn handle_complete_multipart_upload(
|
|||||||
size: total_size,
|
size: total_size,
|
||||||
etag: etag.clone(),
|
etag: etag.clone(),
|
||||||
},
|
},
|
||||||
final_version
|
final_version.blocks.items()[0].1.hash,
|
||||||
.blocks
|
|
||||||
.items()
|
|
||||||
.first()
|
|
||||||
.ok_or_internal_error("Multipart completion produced a final version with no blocks")?
|
|
||||||
.1
|
|
||||||
.hash,
|
|
||||||
));
|
));
|
||||||
|
|
||||||
let final_object = Object::new(*bucket_id, key.clone(), vec![object_version]);
|
let final_object = Object::new(*bucket_id, key.clone(), vec![object_version]);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "garage_block"
|
name = "garage_block"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "garage_db"
|
name = "garage_db"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "garage"
|
name = "garage"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
|
|||||||
+2
-1
@@ -307,7 +307,8 @@ async fn cli_command(opt: Opt) -> Result<(), Error> {
|
|||||||
|
|
||||||
let net_key_hex_str = rpc_secret.ok_or("No RPC secret provided")?;
|
let net_key_hex_str = rpc_secret.ok_or("No RPC secret provided")?;
|
||||||
let network_key = NetworkKey::from_slice(
|
let network_key = NetworkKey::from_slice(
|
||||||
&hex::decode(&net_key_hex_str).err_context("Invalid RPC secret key (bad hex)")?[..],
|
&hex::decode(net_key_hex_str.extract_secret())
|
||||||
|
.err_context("Invalid RPC secret key (bad hex)")?[..],
|
||||||
)
|
)
|
||||||
.ok_or("Invalid RPC secret provided (wrong length)")?;
|
.ok_or("Invalid RPC secret provided (wrong length)")?;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use garage_util::config::Config;
|
use garage_util::config::{Config, Secret};
|
||||||
use garage_util::error::Error;
|
use garage_util::error::Error;
|
||||||
|
|
||||||
/// Structure for secret values or paths that are passed as CLI arguments or environment
|
/// Structure for secret values or paths that are passed as CLI arguments or environment
|
||||||
@@ -99,7 +99,7 @@ pub fn fill_secrets(mut config: Config, secrets: Secrets) -> Result<Config, Erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn fill_secret(
|
pub(crate) fn fill_secret(
|
||||||
config_secret: &mut Option<String>,
|
config_secret: &mut Option<Secret<String>>,
|
||||||
config_secret_file: &Option<PathBuf>,
|
config_secret_file: &Option<PathBuf>,
|
||||||
cli_secret: &Option<String>,
|
cli_secret: &Option<String>,
|
||||||
cli_secret_file: &Option<PathBuf>,
|
cli_secret_file: &Option<PathBuf>,
|
||||||
@@ -110,7 +110,7 @@ pub(crate) fn fill_secret(
|
|||||||
(Some(_), Some(_)) => {
|
(Some(_), Some(_)) => {
|
||||||
return Err(format!("only one of `{}` and `{}_file` can be set", name, name).into());
|
return Err(format!("only one of `{}` and `{}_file` can be set", name, name).into());
|
||||||
}
|
}
|
||||||
(Some(secret), None) => Some(secret.to_string()),
|
(Some(secret), None) => Some(Secret::new(secret.to_string())),
|
||||||
(None, Some(file)) => Some(read_secret_file(file, allow_world_readable)?),
|
(None, Some(file)) => Some(read_secret_file(file, allow_world_readable)?),
|
||||||
(None, None) => None,
|
(None, None) => None,
|
||||||
};
|
};
|
||||||
@@ -132,7 +132,10 @@ pub(crate) fn fill_secret(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_secret_file(file_path: &PathBuf, allow_world_readable: bool) -> Result<String, Error> {
|
fn read_secret_file(
|
||||||
|
file_path: &PathBuf,
|
||||||
|
allow_world_readable: bool,
|
||||||
|
) -> Result<Secret<String>, Error> {
|
||||||
if !allow_world_readable {
|
if !allow_world_readable {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
@@ -152,7 +155,7 @@ fn read_secret_file(file_path: &PathBuf, allow_world_readable: bool) -> Result<S
|
|||||||
|
|
||||||
// trim_end: allows for use case such as `echo "$(openssl rand -hex 32)" > somefile`.
|
// trim_end: allows for use case such as `echo "$(openssl rand -hex 32)" > somefile`.
|
||||||
// also editors sometimes add a trailing newline
|
// also editors sometimes add a trailing newline
|
||||||
Ok(String::from(secret_buf.trim_end()))
|
Ok(Secret::new(String::from(secret_buf.trim_end())))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "garage_model"
|
name = "garage_model"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
|
|||||||
+1
-1
@@ -137,7 +137,7 @@ impl Garage {
|
|||||||
info!("Initializing RPC...");
|
info!("Initializing RPC...");
|
||||||
let network_key = hex::decode(config.rpc_secret.as_ref().ok_or_message(
|
let network_key = hex::decode(config.rpc_secret.as_ref().ok_or_message(
|
||||||
"rpc_secret value is missing, not present in config file or in environment",
|
"rpc_secret value is missing, not present in config file or in environment",
|
||||||
)?)
|
)?.extract_secret())
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|x| NetworkKey::from_slice(&x))
|
.and_then(|x| NetworkKey::from_slice(&x))
|
||||||
.ok_or_message("Invalid RPC secret key: expected 32 bytes of random hex, please check the documentation for requirements")?;
|
.ok_or_message("Invalid RPC secret key: expected 32 bytes of random hex, please check the documentation for requirements")?;
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "garage_net"
|
name = "garage_net"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "garage_rpc"
|
name = "garage_rpc"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
|
|||||||
+1
-1
@@ -115,7 +115,7 @@ impl ConsulDiscovery {
|
|||||||
let mut headers = reqwest::header::HeaderMap::new();
|
let mut headers = reqwest::header::HeaderMap::new();
|
||||||
headers.insert(
|
headers.insert(
|
||||||
"x-consul-token",
|
"x-consul-token",
|
||||||
reqwest::header::HeaderValue::from_str(token)?,
|
reqwest::header::HeaderValue::from_str(token.extract_secret())?,
|
||||||
);
|
);
|
||||||
builder = builder.default_headers(headers);
|
builder = builder.default_headers(headers);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "garage_table"
|
name = "garage_table"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "garage_util"
|
name = "garage_util"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
authors = ["Alex Auvolat <alex@adnab.me>"]
|
authors = ["Alex Auvolat <alex@adnab.me>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
@@ -32,6 +32,7 @@ lazy_static.workspace = true
|
|||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
sha2.workspace = true
|
sha2.workspace = true
|
||||||
|
subtle.workspace = true
|
||||||
|
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
rmp-serde.workspace = true
|
rmp-serde.workspace = true
|
||||||
|
|||||||
+35
-4
@@ -90,7 +90,7 @@ pub struct Config {
|
|||||||
pub allow_world_readable_secrets: bool,
|
pub allow_world_readable_secrets: bool,
|
||||||
|
|
||||||
/// RPC secret key: 32 bytes hex encoded
|
/// RPC secret key: 32 bytes hex encoded
|
||||||
pub rpc_secret: Option<String>,
|
pub rpc_secret: Option<Secret<String>>,
|
||||||
/// Optional file where RPC secret key is read from
|
/// Optional file where RPC secret key is read from
|
||||||
pub rpc_secret_file: Option<PathBuf>,
|
pub rpc_secret_file: Option<PathBuf>,
|
||||||
/// Address to bind for RPC
|
/// Address to bind for RPC
|
||||||
@@ -205,6 +205,37 @@ pub struct WebConfig {
|
|||||||
pub add_host_to_metrics: bool,
|
pub add_host_to_metrics: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct Secret<T>(T);
|
||||||
|
|
||||||
|
impl<T> Secret<T> {
|
||||||
|
pub fn new(secret: T) -> Self {
|
||||||
|
Secret(secret)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extract_secret(&self) -> &T {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: std::ops::Deref<Target = str>> Secret<T> {
|
||||||
|
pub fn eq_ct(&self, other: &T) -> bool {
|
||||||
|
use subtle::ConstantTimeEq;
|
||||||
|
self.0
|
||||||
|
.deref()
|
||||||
|
.as_bytes()
|
||||||
|
.ct_eq(other.deref().as_bytes())
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> std::fmt::Debug for Secret<T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Secret").finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Configuration for the admin and monitoring HTTP API
|
/// Configuration for the admin and monitoring HTTP API
|
||||||
#[derive(Deserialize, Debug, Clone, Default)]
|
#[derive(Deserialize, Debug, Clone, Default)]
|
||||||
pub struct AdminConfig {
|
pub struct AdminConfig {
|
||||||
@@ -212,7 +243,7 @@ pub struct AdminConfig {
|
|||||||
pub api_bind_addr: Option<UnixOrTCPSocketAddress>,
|
pub api_bind_addr: Option<UnixOrTCPSocketAddress>,
|
||||||
|
|
||||||
/// Bearer token to use to scrape metrics
|
/// Bearer token to use to scrape metrics
|
||||||
pub metrics_token: Option<String>,
|
pub metrics_token: Option<Secret<String>>,
|
||||||
/// File to read metrics token from
|
/// File to read metrics token from
|
||||||
pub metrics_token_file: Option<PathBuf>,
|
pub metrics_token_file: Option<PathBuf>,
|
||||||
/// Whether to require an access token for accessing the metrics endpoint
|
/// Whether to require an access token for accessing the metrics endpoint
|
||||||
@@ -220,7 +251,7 @@ pub struct AdminConfig {
|
|||||||
pub metrics_require_token: bool,
|
pub metrics_require_token: bool,
|
||||||
|
|
||||||
/// Bearer token to use to access Admin API endpoints
|
/// Bearer token to use to access Admin API endpoints
|
||||||
pub admin_token: Option<String>,
|
pub admin_token: Option<Secret<String>>,
|
||||||
/// File to read admin token from
|
/// File to read admin token from
|
||||||
pub admin_token_file: Option<PathBuf>,
|
pub admin_token_file: Option<PathBuf>,
|
||||||
|
|
||||||
@@ -252,7 +283,7 @@ pub struct ConsulDiscoveryConfig {
|
|||||||
/// Client TLS key to use when connecting to Consul
|
/// Client TLS key to use when connecting to Consul
|
||||||
pub client_key: Option<String>,
|
pub client_key: Option<String>,
|
||||||
/// /// Token to use for connecting to consul
|
/// /// Token to use for connecting to consul
|
||||||
pub token: Option<String>,
|
pub token: Option<Secret<String>>,
|
||||||
/// Skip TLS hostname verification
|
/// Skip TLS hostname verification
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub tls_skip_verify: bool,
|
pub tls_skip_verify: bool,
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "garage_web"
|
name = "garage_web"
|
||||||
version = "2.4.0"
|
version = "2.3.0"
|
||||||
authors = ["Alex Auvolat <alex@adnab.me>", "Quentin Dufour <quentin@dufour.io>"]
|
authors = ["Alex Auvolat <alex@adnab.me>", "Quentin Dufour <quentin@dufour.io>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user