Compare commits

...

6 Commits

Author SHA1 Message Date
Alex Auvolat 0c431b0c03 admin api: increased compatibility for v0/ endpoints 2023-10-05 16:56:13 +02:00
Alex Auvolat 1c13135f25 admin api: remove broken GET /v0/key router rule 2023-10-05 16:27:29 +02:00
Alex Auvolat 2448eb7713 upgrade doc: fixes and precisions 2023-10-05 15:29:55 +02:00
Alex Auvolat 6790e24f5a Add migration to v0.9 guide 2023-10-05 15:20:48 +02:00
Alex Auvolat 9ccc1d6f4a move upgrade test to release build 2023-10-05 10:42:10 +02:00
Alex Auvolat 920dec393a cli: more precise doc comment 2023-10-04 10:44:42 +02:00
8 changed files with 472 additions and 174 deletions
+13 -9
View File
@@ -42,12 +42,6 @@ steps:
- nix-build --no-build-output --attr clippy.amd64 --argstr git_version ${DRONE_TAG:-$DRONE_COMMIT}
- nix-shell --attr integration --run ./script/test-smoke.sh || (cat /tmp/garage.log; false)
- name: upgrade tests
image: nixpkgs/nix:nixos-22.05
commands:
- nix-build --no-build-output --attr clippy.amd64 --argstr git_version ${DRONE_TAG:-$DRONE_COMMIT}
- nix-shell --attr integration --run "./script/test-upgrade.sh v0.8.4 x86_64-unknown-linux-musl" || (cat /tmp/garage.log; false)
trigger:
event:
- custom
@@ -71,11 +65,16 @@ steps:
- nix-build --no-build-output --attr pkgs.amd64.release --argstr git_version ${DRONE_TAG:-$DRONE_COMMIT}
- nix-shell --attr rust --run "./script/not-dynamic.sh result-bin/bin/garage"
- name: integration
- name: integration tests
image: nixpkgs/nix:nixos-22.05
commands:
- nix-shell --attr integration --run ./script/test-smoke.sh || (cat /tmp/garage.log; false)
- name: upgrade tests
image: nixpkgs/nix:nixos-22.05
commands:
- nix-shell --attr integration --run "./script/test-upgrade.sh v0.8.4 x86_64-unknown-linux-musl" || (cat /tmp/garage.log; false)
- name: push static binary
image: nixpkgs/nix:nixos-22.05
environment:
@@ -122,11 +121,16 @@ steps:
- nix-build --no-build-output --attr pkgs.i386.release --argstr git_version ${DRONE_TAG:-$DRONE_COMMIT}
- nix-shell --attr rust --run "./script/not-dynamic.sh result-bin/bin/garage"
- name: integration
- name: integration tests
image: nixpkgs/nix:nixos-22.05
commands:
- nix-shell --attr integration --run ./script/test-smoke.sh || (cat /tmp/garage.log; false)
- name: upgrade tests
image: nixpkgs/nix:nixos-22.05
commands:
- nix-shell --attr integration --run "./script/test-upgrade.sh v0.8.4 i686-unknown-linux-musl" || (cat /tmp/garage.log; false)
- name: push static binary
image: nixpkgs/nix:nixos-22.05
environment:
@@ -291,6 +295,6 @@ trigger:
---
kind: signature
hmac: dcf68ed6536e3fd9b359377e4c6c1d321fa9cc0f6fe5645d69c8f1d604569b4c
hmac: 0c4b57eb4b27b7c6a6ff21ab87f0767fe3eb90f5d95d5cbcdccf794e9d2a5d86
...
@@ -0,0 +1,69 @@
+++
title = "Migrating from 0.8 to 0.9"
weight = 14
+++
**This guide explains how to migrate to 0.9 if you have an existing 0.8 cluster.
We don't recommend trying to migrate to 0.9 directly from 0.7 or older.**
**We make no guarantee that this migration will work perfectly:
back up all your data before attempting it!**
The following are **breaking changes** in Garage v0.9 that require your attention when migrating:
- LMDB is now the default metadata db engine and Sled is deprecated. If you were using Sled, make sure to specify `db_engine = "sled"` in your configuration file, or take the time to [convert your database](https://garagehq.deuxfleurs.fr/documentation/reference-manual/configuration/#db-engine-since-v0-8-0).
- Capacity values are now in actual byte units. The translation from the old layout will assign 1 capacity = 1Gb by default, which might be wrong for your cluster. This does not cause any data to be moved around, but you might want to re-assign correct capacity values post-migration.
- Multipart uploads that were started in Garage v0.8 will not be visible in Garage v0.9 and will have to be restarted from scratch.
- Changes to the admin API: some `v0/` endpoints have been replaced by `v1/` counterparts with updated/uniformized syntax. All other endpoints have also moved to `v1/` by default, without syntax changes, but are still available under `v0/` for compatibility.
## Simple migration procedure (takes cluster offline for a while)
The migration steps are as follows:
1. Disable API and web access. You may do this by stopping your reverse proxy or by commenting out
the `api_bind_addr` values in your `config.toml` file and restarting Garage.
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 (`versions`, `block_refs`, etc.)
3. Check that the block resync queue and Merkle queue are empty:
run `garage stats -a` to query them or inspect metrics in the Grafana dashboard.
4. Turn off Garage v0.8
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.8.tar.zst meta/`
6. Install Garage v0.9
7. Update your configuration file if necessary.
8. Turn on Garage v0.9
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.
12. You might want to assign correct capacity values to all your nodes. Doing so might cause data to be moved
in your cluster, which should also be monitored carefully.
## Minimal downtime migration procedure
The migration to Garage v0.9 can be done with almost no downtime,
by restarting all nodes at once in the new version.
The migration steps are as follows:
1. Do `garage repair --all-nodes --yes tables` and `garage repair --all-nodes --yes blocks`,
check the logs and check that all data seems to be synced correctly between
nodes. If you have time, do additional checks (`versions`, `block_refs`, etc.)
2. Turn off each node individually; back up its metadata folder (see above); turn it back on again.
This will allow you to take a backup of all nodes without impacting global cluster availability.
You can do all nodes of a single zone at once as this does not impact the availability of Garage.
3. Prepare your binaries and configuration files for Garage v0.9
4. Shut down all v0.8 nodes simultaneously, and restart them all simultaneously in v0.9.
Use your favorite deployment tool (Ansible, Kubernetes, Nomad) to achieve this as fast as possible.
Garage v0.9 should be in a working state as soon as it starts.
5. Proceed with repair and monitoring as described in steps 9-12 above.
+8 -2
View File
@@ -25,7 +25,8 @@ use crate::admin::bucket::*;
use crate::admin::cluster::*;
use crate::admin::error::*;
use crate::admin::key::*;
use crate::admin::router::{Authorization, Endpoint};
use crate::admin::router_v0;
use crate::admin::router_v1::{Authorization, Endpoint};
use crate::helpers::host_to_bucket;
pub struct AdminApiServer {
@@ -229,7 +230,12 @@ impl ApiHandler for AdminApiServer {
type Error = Error;
fn parse_endpoint(&self, req: &Request<Body>) -> Result<Endpoint, Error> {
Endpoint::from_request(req)
if req.uri().path().starts_with("/v0/") {
let endpoint_v0 = router_v0::Endpoint::from_request(req)?;
Endpoint::from_v0(endpoint_v0)
} else {
Endpoint::from_request(req)
}
}
async fn handle(
+2 -1
View File
@@ -1,6 +1,7 @@
pub mod api_server;
mod error;
mod router;
mod router_v0;
mod router_v1;
mod bucket;
mod cluster;
-160
View File
@@ -1,160 +0,0 @@
use std::borrow::Cow;
use hyper::{Method, Request};
use crate::admin::error::*;
use crate::router_macros::*;
pub enum Authorization {
None,
MetricsToken,
AdminToken,
}
router_match! {@func
/// List of all Admin API endpoints.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Endpoint {
Options,
CheckDomain,
Health,
Metrics,
GetClusterStatus,
GetClusterHealth,
ConnectClusterNodes,
// Layout
GetClusterLayout,
UpdateClusterLayout,
ApplyClusterLayout,
RevertClusterLayout,
// Keys
ListKeys,
CreateKey,
ImportKey,
GetKeyInfo {
id: Option<String>,
search: Option<String>,
show_secret_key: Option<String>,
},
DeleteKey {
id: String,
},
UpdateKey {
id: String,
},
// Buckets
ListBuckets,
CreateBucket,
GetBucketInfo {
id: Option<String>,
global_alias: Option<String>,
},
DeleteBucket {
id: String,
},
UpdateBucket {
id: String,
},
// Bucket-Key Permissions
BucketAllowKey,
BucketDenyKey,
// Bucket aliases
GlobalAliasBucket {
id: String,
alias: String,
},
GlobalUnaliasBucket {
id: String,
alias: String,
},
LocalAliasBucket {
id: String,
access_key_id: String,
alias: String,
},
LocalUnaliasBucket {
id: String,
access_key_id: String,
alias: String,
},
}}
impl Endpoint {
/// Determine which S3 endpoint a request is for using the request, and a bucket which was
/// possibly extracted from the Host header.
/// Returns Self plus bucket name, if endpoint is not Endpoint::ListBuckets
pub fn from_request<T>(req: &Request<T>) -> Result<Self, Error> {
let uri = req.uri();
let path = uri.path();
let query = uri.query();
let mut query = QueryParameters::from_query(query.unwrap_or_default())?;
let res = router_match!(@gen_path_parser (req.method(), path, query) [
OPTIONS _ => Options,
GET "/check" => CheckDomain,
GET "/health" => Health,
GET "/metrics" => Metrics,
GET "/v1/status" => GetClusterStatus,
GET "/v1/health" => GetClusterHealth,
POST ("/v0/connect" | "/v1/connect") => ConnectClusterNodes,
// Layout endpoints
GET "/v1/layout" => GetClusterLayout,
POST "/v1/layout" => UpdateClusterLayout,
POST "/v1/layout/apply" => ApplyClusterLayout,
POST ("/v0/layout/revert" | "/v1/layout/revert") => RevertClusterLayout,
// API key endpoints
GET "/v1/key" if id => GetKeyInfo (query_opt::id, query_opt::search, query_opt::show_secret_key),
GET "/v1/key" if search => GetKeyInfo (query_opt::id, query_opt::search, query_opt::show_secret_key),
POST "/v1/key" if id => UpdateKey (query::id),
POST "/v1/key" => CreateKey,
POST "/v1/key/import" => ImportKey,
DELETE ("/v0/key" | "/v1/key") if id => DeleteKey (query::id),
GET ("/v0/key" | "/v1/key") => ListKeys,
// Bucket endpoints
GET ("/v0/bucket" | "/v1/bucket") if id => GetBucketInfo (query_opt::id, query_opt::global_alias),
GET ("/v0/bucket" | "/v1/bucket") if global_alias => GetBucketInfo (query_opt::id, query_opt::global_alias),
GET ("/v0/bucket" | "/v1/bucket") => ListBuckets,
POST ("/v0/bucket" | "/v1/bucket") => CreateBucket,
DELETE ("/v0/bucket" | "/v1/bucket") if id => DeleteBucket (query::id),
PUT ("/v0/bucket" | "/v1/bucket") if id => UpdateBucket (query::id),
// Bucket-key permissions
POST ("/v0/bucket/allow" | "/v1/bucket/allow") => BucketAllowKey,
POST ("/v0/bucket/deny" | "/v1/bucket/deny") => BucketDenyKey,
// Bucket aliases
PUT ("/v0/bucket/alias/global" | "/v1/bucket/alias/global") => GlobalAliasBucket (query::id, query::alias),
DELETE ("/v0/bucket/alias/global" | "/v1/bucket/alias/global") => GlobalUnaliasBucket (query::id, query::alias),
PUT ("/v0/bucket/alias/local" | "/v1/bucket/alias/local") => LocalAliasBucket (query::id, query::access_key_id, query::alias),
DELETE ("/v0/bucket/alias/local" | "/v1/bucket/alias/local") => LocalUnaliasBucket (query::id, query::access_key_id, query::alias),
]);
if let Some(message) = query.nonempty_message() {
debug!("Unused query parameter: {}", message)
}
Ok(res)
}
/// Get the kind of authorization which is required to perform the operation.
pub fn authorization_type(&self) -> Authorization {
match self {
Self::Health => Authorization::None,
Self::CheckDomain => Authorization::None,
Self::Metrics => Authorization::MetricsToken,
_ => Authorization::AdminToken,
}
}
}
generateQueryParameters! {
keywords: [],
fields: [
"format" => format,
"id" => id,
"search" => search,
"globalAlias" => global_alias,
"alias" => alias,
"accessKeyId" => access_key_id,
"showSecretKey" => show_secret_key
]
}
+143
View File
@@ -0,0 +1,143 @@
use std::borrow::Cow;
use hyper::{Method, Request};
use crate::admin::error::*;
use crate::router_macros::*;
router_match! {@func
/// List of all Admin API endpoints.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Endpoint {
Options,
CheckDomain,
Health,
Metrics,
GetClusterStatus,
GetClusterHealth,
ConnectClusterNodes,
// Layout
GetClusterLayout,
UpdateClusterLayout,
ApplyClusterLayout,
RevertClusterLayout,
// Keys
ListKeys,
CreateKey,
ImportKey,
GetKeyInfo {
id: Option<String>,
search: Option<String>,
},
DeleteKey {
id: String,
},
UpdateKey {
id: String,
},
// Buckets
ListBuckets,
CreateBucket,
GetBucketInfo {
id: Option<String>,
global_alias: Option<String>,
},
DeleteBucket {
id: String,
},
UpdateBucket {
id: String,
},
// Bucket-Key Permissions
BucketAllowKey,
BucketDenyKey,
// Bucket aliases
GlobalAliasBucket {
id: String,
alias: String,
},
GlobalUnaliasBucket {
id: String,
alias: String,
},
LocalAliasBucket {
id: String,
access_key_id: String,
alias: String,
},
LocalUnaliasBucket {
id: String,
access_key_id: String,
alias: String,
},
}}
impl Endpoint {
/// Determine which S3 endpoint a request is for using the request, and a bucket which was
/// possibly extracted from the Host header.
/// Returns Self plus bucket name, if endpoint is not Endpoint::ListBuckets
pub fn from_request<T>(req: &Request<T>) -> Result<Self, Error> {
let uri = req.uri();
let path = uri.path();
let query = uri.query();
let mut query = QueryParameters::from_query(query.unwrap_or_default())?;
let res = router_match!(@gen_path_parser (req.method(), path, query) [
OPTIONS _ => Options,
GET "/check" => CheckDomain,
GET "/health" => Health,
GET "/metrics" => Metrics,
GET "/v0/status" => GetClusterStatus,
GET "/v0/health" => GetClusterHealth,
POST "/v0/connect" => ConnectClusterNodes,
// Layout endpoints
GET "/v0/layout" => GetClusterLayout,
POST "/v0/layout" => UpdateClusterLayout,
POST "/v0/layout/apply" => ApplyClusterLayout,
POST "/v0/layout/revert" => RevertClusterLayout,
// API key endpoints
GET "/v0/key" if id => GetKeyInfo (query_opt::id, query_opt::search),
GET "/v0/key" if search => GetKeyInfo (query_opt::id, query_opt::search),
POST "/v0/key" if id => UpdateKey (query::id),
POST "/v0/key" => CreateKey,
POST "/v0/key/import" => ImportKey,
DELETE "/v0/key" if id => DeleteKey (query::id),
GET "/v0/key" => ListKeys,
// Bucket endpoints
GET "/v0/bucket" if id => GetBucketInfo (query_opt::id, query_opt::global_alias),
GET "/v0/bucket" if global_alias => GetBucketInfo (query_opt::id, query_opt::global_alias),
GET "/v0/bucket" => ListBuckets,
POST "/v0/bucket" => CreateBucket,
DELETE "/v0/bucket" if id => DeleteBucket (query::id),
PUT "/v0/bucket" if id => UpdateBucket (query::id),
// Bucket-key permissions
POST "/v0/bucket/allow" => BucketAllowKey,
POST "/v0/bucket/deny" => BucketDenyKey,
// Bucket aliases
PUT "/v0/bucket/alias/global" => GlobalAliasBucket (query::id, query::alias),
DELETE "/v0/bucket/alias/global" => GlobalUnaliasBucket (query::id, query::alias),
PUT "/v0/bucket/alias/local" => LocalAliasBucket (query::id, query::access_key_id, query::alias),
DELETE "/v0/bucket/alias/local" => LocalUnaliasBucket (query::id, query::access_key_id, query::alias),
]);
if let Some(message) = query.nonempty_message() {
debug!("Unused query parameter: {}", message)
}
Ok(res)
}
}
generateQueryParameters! {
keywords: [],
fields: [
"format" => format,
"id" => id,
"search" => search,
"globalAlias" => global_alias,
"alias" => alias,
"accessKeyId" => access_key_id
]
}
+235
View File
@@ -0,0 +1,235 @@
use std::borrow::Cow;
use hyper::{Method, Request};
use crate::admin::error::*;
use crate::admin::router_v0;
use crate::router_macros::*;
pub enum Authorization {
None,
MetricsToken,
AdminToken,
}
router_match! {@func
/// List of all Admin API endpoints.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Endpoint {
Options,
CheckDomain,
Health,
Metrics,
GetClusterStatus,
GetClusterHealth,
ConnectClusterNodes,
// Layout
GetClusterLayout,
UpdateClusterLayout,
ApplyClusterLayout,
RevertClusterLayout,
// Keys
ListKeys,
CreateKey,
ImportKey,
GetKeyInfo {
id: Option<String>,
search: Option<String>,
show_secret_key: Option<String>,
},
DeleteKey {
id: String,
},
UpdateKey {
id: String,
},
// Buckets
ListBuckets,
CreateBucket,
GetBucketInfo {
id: Option<String>,
global_alias: Option<String>,
},
DeleteBucket {
id: String,
},
UpdateBucket {
id: String,
},
// Bucket-Key Permissions
BucketAllowKey,
BucketDenyKey,
// Bucket aliases
GlobalAliasBucket {
id: String,
alias: String,
},
GlobalUnaliasBucket {
id: String,
alias: String,
},
LocalAliasBucket {
id: String,
access_key_id: String,
alias: String,
},
LocalUnaliasBucket {
id: String,
access_key_id: String,
alias: String,
},
}}
impl Endpoint {
/// Determine which S3 endpoint a request is for using the request, and a bucket which was
/// possibly extracted from the Host header.
/// Returns Self plus bucket name, if endpoint is not Endpoint::ListBuckets
pub fn from_request<T>(req: &Request<T>) -> Result<Self, Error> {
let uri = req.uri();
let path = uri.path();
let query = uri.query();
let mut query = QueryParameters::from_query(query.unwrap_or_default())?;
let res = router_match!(@gen_path_parser (req.method(), path, query) [
OPTIONS _ => Options,
GET "/check" => CheckDomain,
GET "/health" => Health,
GET "/metrics" => Metrics,
GET "/v1/status" => GetClusterStatus,
GET "/v1/health" => GetClusterHealth,
POST "/v1/connect" => ConnectClusterNodes,
// Layout endpoints
GET "/v1/layout" => GetClusterLayout,
POST "/v1/layout" => UpdateClusterLayout,
POST "/v1/layout/apply" => ApplyClusterLayout,
POST "/v1/layout/revert" => RevertClusterLayout,
// API key endpoints
GET "/v1/key" if id => GetKeyInfo (query_opt::id, query_opt::search, query_opt::show_secret_key),
GET "/v1/key" if search => GetKeyInfo (query_opt::id, query_opt::search, query_opt::show_secret_key),
POST "/v1/key" if id => UpdateKey (query::id),
POST "/v1/key" => CreateKey,
POST "/v1/key/import" => ImportKey,
DELETE "/v1/key" if id => DeleteKey (query::id),
GET "/v1/key" => ListKeys,
// Bucket endpoints
GET "/v1/bucket" if id => GetBucketInfo (query_opt::id, query_opt::global_alias),
GET "/v1/bucket" if global_alias => GetBucketInfo (query_opt::id, query_opt::global_alias),
GET "/v1/bucket" => ListBuckets,
POST "/v1/bucket" => CreateBucket,
DELETE "/v1/bucket" if id => DeleteBucket (query::id),
PUT "/v1/bucket" if id => UpdateBucket (query::id),
// Bucket-key permissions
POST "/v1/bucket/allow" => BucketAllowKey,
POST "/v1/bucket/deny" => BucketDenyKey,
// Bucket aliases
PUT "/v1/bucket/alias/global" => GlobalAliasBucket (query::id, query::alias),
DELETE "/v1/bucket/alias/global" => GlobalUnaliasBucket (query::id, query::alias),
PUT "/v1/bucket/alias/local" => LocalAliasBucket (query::id, query::access_key_id, query::alias),
DELETE "/v1/bucket/alias/local" => LocalUnaliasBucket (query::id, query::access_key_id, query::alias),
]);
if let Some(message) = query.nonempty_message() {
debug!("Unused query parameter: {}", message)
}
Ok(res)
}
/// Some endpoints work exactly the same in their v1/ version as they did in their v0/ version.
/// For these endpoints, we can convert a v0/ call to its equivalent as if it was made using
/// its v1/ URL.
pub fn from_v0(v0_endpoint: router_v0::Endpoint) -> Result<Self, Error> {
match v0_endpoint {
// Cluster endpoints
router_v0::Endpoint::ConnectClusterNodes => Ok(Self::ConnectClusterNodes),
// - GetClusterStatus: response format changed
// - GetClusterHealth: response format changed
// Layout endpoints
router_v0::Endpoint::RevertClusterLayout => Ok(Self::RevertClusterLayout),
// - GetClusterLayout: response format changed
// - UpdateClusterLayout: query format changed
// - ApplyCusterLayout: response format changed
// Key endpoints
router_v0::Endpoint::ListKeys => Ok(Self::ListKeys),
router_v0::Endpoint::CreateKey => Ok(Self::CreateKey),
router_v0::Endpoint::GetKeyInfo { id, search } => Ok(Self::GetKeyInfo {
id,
search,
show_secret_key: Some("true".into()),
}),
router_v0::Endpoint::DeleteKey { id } => Ok(Self::DeleteKey { id }),
// - UpdateKey: response format changed (secret key no longer returned)
// Bucket endpoints
router_v0::Endpoint::GetBucketInfo { id, global_alias } => {
Ok(Self::GetBucketInfo { id, global_alias })
}
router_v0::Endpoint::ListBuckets => Ok(Self::ListBuckets),
router_v0::Endpoint::CreateBucket => Ok(Self::CreateBucket),
router_v0::Endpoint::DeleteBucket { id } => Ok(Self::DeleteBucket { id }),
router_v0::Endpoint::UpdateBucket { id } => Ok(Self::UpdateBucket { id }),
// Bucket-key permissions
router_v0::Endpoint::BucketAllowKey => Ok(Self::BucketAllowKey),
router_v0::Endpoint::BucketDenyKey => Ok(Self::BucketDenyKey),
// Bucket alias endpoints
router_v0::Endpoint::GlobalAliasBucket { id, alias } => {
Ok(Self::GlobalAliasBucket { id, alias })
}
router_v0::Endpoint::GlobalUnaliasBucket { id, alias } => {
Ok(Self::GlobalUnaliasBucket { id, alias })
}
router_v0::Endpoint::LocalAliasBucket {
id,
access_key_id,
alias,
} => Ok(Self::LocalAliasBucket {
id,
access_key_id,
alias,
}),
router_v0::Endpoint::LocalUnaliasBucket {
id,
access_key_id,
alias,
} => Ok(Self::LocalUnaliasBucket {
id,
access_key_id,
alias,
}),
// For endpoints that have different body content syntax, issue
// deprecation warning
_ => Err(Error::bad_request(format!(
"v0/ endpoint is no longer supported: {}",
v0_endpoint.name()
))),
}
}
/// Get the kind of authorization which is required to perform the operation.
pub fn authorization_type(&self) -> Authorization {
match self {
Self::Health => Authorization::None,
Self::CheckDomain => Authorization::None,
Self::Metrics => Authorization::MetricsToken,
_ => Authorization::AdminToken,
}
}
}
generateQueryParameters! {
keywords: [],
fields: [
"format" => format,
"id" => id,
"search" => search,
"globalAlias" => global_alias,
"alias" => alias,
"accessKeyId" => access_key_id,
"showSecretKey" => show_secret_key
]
}
+2 -2
View File
@@ -453,7 +453,7 @@ pub enum RepairWhat {
/// Do a full sync of metadata tables
#[structopt(name = "tables", version = garage_version())]
Tables,
/// Repair (resync/rebalance) the set of stored blocks
/// Repair (resync/rebalance) the set of stored blocks in the cluster
#[structopt(name = "blocks", version = garage_version())]
Blocks,
/// Repropagate object deletions to the version table
@@ -471,7 +471,7 @@ pub enum RepairWhat {
#[structopt(subcommand)]
cmd: ScrubCmd,
},
/// Rebalance data blocks among storage locations
/// Rebalance data blocks among HDDs on individual nodes
#[structopt(name = "rebalance", version = garage_version())]
Rebalance,
}