Merge pull request 'admin api improvements' (#983) from admin-api-improvements into next-v2
All checks were successful
ci/woodpecker/push/debug Pipeline was successful
ci/woodpecker/pr/debug Pipeline was successful

Reviewed-on: #983
This commit is contained in:
Alex 2025-03-12 11:00:24 +00:00
commit d032e2017c
3 changed files with 196 additions and 26 deletions

View file

@ -21,6 +21,74 @@
}
],
"paths": {
"/check": {
"get": {
"tags": [
"Special endpoints"
],
"description": "\nStatic website domain name check. Checks whether a bucket is configured to serve\na static website for the requested domain. This is used by reverse proxies such\nas Caddy or Tricot, to avoid requesting TLS certificates for domain names that\ndo not correspond to an actual website.\n ",
"operationId": "CheckDomain",
"parameters": [
{
"name": "domain",
"in": "path",
"description": "The domain name to check for",
"required": true
}
],
"responses": {
"200": {
"description": "The domain name redirects to a static website bucket"
},
"400": {
"description": "No static website bucket exists for this domain"
}
},
"security": [
{}
]
}
},
"/health": {
"get": {
"tags": [
"Special endpoints"
],
"description": "\nCheck cluster health. The status code returned by this function indicates\nwhether this Garage daemon can answer API requests.\nGarage will return `200 OK` even if some storage nodes are disconnected,\nas long as it is able to have a quorum of nodes for read and write operations.\n ",
"operationId": "Health",
"responses": {
"200": {
"description": "Garage is able to answer requests"
},
"503": {
"description": "This Garage daemon is not able to handle requests"
}
},
"security": [
{}
]
}
},
"/metrics": {
"get": {
"tags": [
"Special endpoints"
],
"description": "Prometheus metrics endpoint",
"operationId": "Metrics",
"responses": {
"200": {
"description": "Garage daemon metrics exported in Prometheus format"
}
},
"security": [
{},
{
"bearerAuth": []
}
]
}
},
"/v2/AddBucketAlias": {
"post": {
"tags": [
@ -486,13 +554,25 @@
"name": "id",
"in": "path",
"description": "Admin API token ID",
"required": true
"required": true,
"schema": {
"type": [
"string",
"null"
]
}
},
{
"name": "search",
"in": "path",
"description": "Partial token ID or name to search for",
"required": true
"required": true,
"schema": {
"type": [
"string",
"null"
]
}
}
],
"responses": {
@ -566,19 +646,37 @@
"name": "id",
"in": "path",
"description": "Exact bucket ID to look up",
"required": true
"required": true,
"schema": {
"type": [
"string",
"null"
]
}
},
{
"name": "globalAlias",
"in": "path",
"description": "Global alias of bucket to look up",
"required": true
"required": true,
"schema": {
"type": [
"string",
"null"
]
}
},
{
"name": "search",
"in": "path",
"description": "Partial ID or alias to search for",
"required": true
"required": true,
"schema": {
"type": [
"string",
"null"
]
}
}
],
"responses": {
@ -727,19 +825,34 @@
"name": "id",
"in": "path",
"description": "Access key ID",
"required": true
"required": true,
"schema": {
"type": [
"string",
"null"
]
}
},
{
"name": "search",
"in": "path",
"description": "Partial key ID or name to search for",
"required": true
"required": true,
"schema": {
"type": [
"string",
"null"
]
}
},
{
"name": "showSecretKey",
"in": "path",
"description": "Whether to return the secret access key",
"required": true
"required": true,
"schema": {
"type": "boolean"
}
}
],
"responses": {

View file

@ -5,7 +5,7 @@ use std::sync::Arc;
use paste::paste;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use utoipa::{IntoParams, ToSchema};
use garage_rpc::*;
@ -303,9 +303,12 @@ pub struct ListAdminTokensResponse(pub Vec<GetAdminTokenInfoResponse>);
// ---- GetAdminTokenInfo ----
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, IntoParams)]
#[serde(rename_all = "camelCase")]
pub struct GetAdminTokenInfoRequest {
/// Admin API token ID
pub id: Option<String>,
/// Partial token ID or name to search for
pub search: Option<String>,
}
@ -634,10 +637,15 @@ pub struct ListKeysResponseItem {
// ---- GetKeyInfo ----
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, IntoParams)]
#[serde(rename_all = "camelCase")]
pub struct GetKeyInfoRequest {
/// Access key ID
pub id: Option<String>,
/// Partial key ID or name to search for
pub search: Option<String>,
/// Whether to return the secret access key
#[serde(default)]
pub show_secret_key: bool,
}
@ -761,10 +769,14 @@ pub struct BucketLocalAlias {
// ---- GetBucketInfo ----
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, IntoParams)]
#[serde(rename_all = "camelCase")]
pub struct GetBucketInfoRequest {
/// Exact bucket ID to look up
pub id: Option<String>,
/// Global alias of bucket to look up
pub global_alias: Option<String>,
/// Partial ID or alias to search for
pub search: Option<String>,
}

View file

@ -5,6 +5,58 @@ use utoipa::{Modify, OpenApi};
use crate::api::*;
// **********************************************
// Special endpoints
// **********************************************
#[utoipa::path(get,
path = "/metrics",
tag = "Special endpoints",
description = "Prometheus metrics endpoint",
security((), ("bearerAuth" = [])),
responses(
(status = 200, description = "Garage daemon metrics exported in Prometheus format"),
),
)]
fn Metrics() -> () {}
#[utoipa::path(get,
path = "/health",
tag = "Special endpoints",
description = "
Check cluster health. The status code returned by this function indicates
whether this Garage daemon can answer API requests.
Garage will return `200 OK` even if some storage nodes are disconnected,
as long as it is able to have a quorum of nodes for read and write operations.
",
security(()),
responses(
(status = 200, description = "Garage is able to answer requests"),
(status = 503, description = "This Garage daemon is not able to handle requests")
),
)]
fn Health() -> () {}
#[utoipa::path(get,
path = "/check",
tag = "Special endpoints",
description = "
Static website domain name check. Checks whether a bucket is configured to serve
a static website for the requested domain. This is used by reverse proxies such
as Caddy or Tricot, to avoid requesting TLS certificates for domain names that
do not correspond to an actual website.
",
params(
("domain", description = "The domain name to check for"),
),
security(()),
responses(
(status = 200, description = "The domain name redirects to a static website bucket"),
(status = 400, description = "No static website bucket exists for this domain")
),
)]
fn CheckDomain() -> () {}
// **********************************************
// Cluster operations
// **********************************************
@ -88,10 +140,7 @@ fn ListAdminTokens() -> () {}
Return information about a specific admin API token.
You can search by specifying the exact token identifier (`id`) or by specifying a pattern (`search`).
",
params(
("id", description = "Admin API token ID"),
("search", description = "Partial token ID or name to search for"),
),
params(GetAdminTokenInfoRequest),
responses(
(status = 200, description = "Information about the admin token", body = GetAdminTokenInfoResponse),
(status = 500, description = "Internal server error")
@ -284,11 +333,7 @@ You can search by specifying the exact key identifier (`id`) or by specifying a
For confidentiality reasons, the secret key is not returned by default: you must pass the `showSecretKey` query parameter to get it.
",
params(
("id", description = "Access key ID"),
("search", description = "Partial key ID or name to search for"),
("showSecretKey", description = "Whether to return the secret access key"),
),
params(GetKeyInfoRequest),
responses(
(status = 200, description = "Information about the access key", body = GetKeyInfoResponse),
(status = 500, description = "Internal server error")
@ -381,11 +426,7 @@ It includes its aliases, its web configuration, keys that have some permissions
on it, some statistics (number of objects, size), number of dangling multipart uploads,
and its quotas (if any).
",
params(
("id", description = "Exact bucket ID to look up"),
("globalAlias", description = "Global alias of bucket to look up"),
("search", description = "Partial ID or alias to search for"),
),
params(GetBucketInfoRequest),
responses(
(status = 200, description = "Returns exhaustive information about the bucket", body = GetBucketInfoResponse),
(status = 500, description = "Internal server error")
@ -794,6 +835,10 @@ impl Modify for SecurityAddon {
modifiers(&SecurityAddon),
security(("bearerAuth" = [])),
paths(
// Special ops
Metrics,
Health,
CheckDomain,
// Cluster operations
GetClusterHealth,
GetClusterStatus,