publish bucket creation date in admin api and CLI

This commit is contained in:
Alex Auvolat
2025-04-17 10:29:23 +02:00
parent 2d1c073d2f
commit 2f21181ccb
4 changed files with 32 additions and 7 deletions
+11
View File
@@ -2280,6 +2280,7 @@
"type": "object", "type": "object",
"required": [ "required": [
"id", "id",
"created",
"globalAliases", "globalAliases",
"websiteAccess", "websiteAccess",
"keys", "keys",
@@ -2297,6 +2298,11 @@
"format": "int64", "format": "int64",
"description": "Total number of bytes used by objects in this bucket" "description": "Total number of bytes used by objects in this bucket"
}, },
"created": {
"type": "string",
"format": "date-time",
"description": "Bucket creation date"
},
"globalAliases": { "globalAliases": {
"type": "array", "type": "array",
"items": { "items": {
@@ -2873,10 +2879,15 @@
"type": "object", "type": "object",
"required": [ "required": [
"id", "id",
"created",
"globalAliases", "globalAliases",
"localAliases" "localAliases"
], ],
"properties": { "properties": {
"created": {
"type": "string",
"format": "date-time"
},
"globalAliases": { "globalAliases": {
"type": "array", "type": "array",
"items": { "items": {
+8 -4
View File
@@ -3,6 +3,7 @@ use std::convert::TryFrom;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::Arc; use std::sync::Arc;
use chrono::{DateTime, Utc};
use paste::paste; use paste::paste;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use utoipa::{IntoParams, ToSchema}; use utoipa::{IntoParams, ToSchema};
@@ -321,11 +322,11 @@ pub struct GetAdminTokenInfoResponse {
/// Identifier of the admin token (which is also a prefix of the full bearer token) /// Identifier of the admin token (which is also a prefix of the full bearer token)
pub id: Option<String>, pub id: Option<String>,
/// Creation date /// Creation date
pub created: Option<chrono::DateTime<chrono::Utc>>, pub created: Option<DateTime<Utc>>,
/// Name of the admin API token /// Name of the admin API token
pub name: String, pub name: String,
/// Expiration time and date, formatted according to RFC 3339 /// Expiration time and date, formatted according to RFC 3339
pub expiration: Option<chrono::DateTime<chrono::Utc>>, pub expiration: Option<DateTime<Utc>>,
/// Whether this admin token is expired already /// Whether this admin token is expired already
pub expired: bool, pub expired: bool,
/// Scope of the admin API token, a list of admin endpoint names (such as /// Scope of the admin API token, a list of admin endpoint names (such as
@@ -364,7 +365,7 @@ pub struct UpdateAdminTokenRequestBody {
/// Name of the admin API token /// Name of the admin API token
pub name: Option<String>, pub name: Option<String>,
/// Expiration time and date, formatted according to RFC 3339 /// Expiration time and date, formatted according to RFC 3339
pub expiration: Option<chrono::DateTime<chrono::Utc>>, pub expiration: Option<DateTime<Utc>>,
/// Scope of the admin API token, a list of admin endpoint names (such as /// Scope of the admin API token, a list of admin endpoint names (such as
/// `GetClusterStatus`, etc), or the special value `*` to allow all /// `GetClusterStatus`, etc), or the special value `*` to allow all
/// admin endpoints. **WARNING:** Granting a scope of `CreateAdminToken` or /// admin endpoints. **WARNING:** Granting a scope of `CreateAdminToken` or
@@ -759,6 +760,7 @@ pub struct ListBucketsResponse(pub Vec<ListBucketsResponseItem>);
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct ListBucketsResponseItem { pub struct ListBucketsResponseItem {
pub id: String, pub id: String,
pub created: DateTime<Utc>,
pub global_aliases: Vec<String>, pub global_aliases: Vec<String>,
pub local_aliases: Vec<BucketLocalAlias>, pub local_aliases: Vec<BucketLocalAlias>,
} }
@@ -788,6 +790,8 @@ pub struct GetBucketInfoRequest {
pub struct GetBucketInfoResponse { pub struct GetBucketInfoResponse {
/// Identifier of the bucket /// Identifier of the bucket
pub id: String, pub id: String,
/// Bucket creation date
pub created: DateTime<Utc>,
/// List of global aliases for this bucket /// List of global aliases for this bucket
pub global_aliases: Vec<String>, pub global_aliases: Vec<String>,
/// Whether website acces is enabled for this bucket /// Whether website acces is enabled for this bucket
@@ -932,7 +936,7 @@ pub struct InspectObjectVersion {
/// Version ID /// Version ID
pub uuid: String, pub uuid: String,
/// Creation timestamp of this object version /// Creation timestamp of this object version
pub timestamp: chrono::DateTime<chrono::Utc>, pub timestamp: DateTime<Utc>,
/// Whether this object version was created with SSE-C encryption /// Whether this object version was created with SSE-C encryption
pub encrypted: bool, pub encrypted: bool,
/// Whether this object version is still uploading /// Whether this object version is still uploading
+4
View File
@@ -48,6 +48,8 @@ impl RequestHandler for ListBucketsRequest {
let state = b.state.as_option().unwrap(); let state = b.state.as_option().unwrap();
ListBucketsResponseItem { ListBucketsResponseItem {
id: hex::encode(b.id), id: hex::encode(b.id),
created: DateTime::from_timestamp_millis(state.creation_date as i64)
.expect("invalid timestamp stored in db"),
global_aliases: state global_aliases: state
.aliases .aliases
.items() .items()
@@ -677,6 +679,8 @@ async fn bucket_info_results(
let quotas = state.quotas.get(); let quotas = state.quotas.get();
let res = GetBucketInfoResponse { let res = GetBucketInfoResponse {
id: hex::encode(bucket.id), id: hex::encode(bucket.id),
created: DateTime::from_timestamp_millis(state.creation_date as i64)
.expect("invalid timestamp stored in db"),
global_aliases: state global_aliases: state
.aliases .aliases
.items() .items()
+9 -3
View File
@@ -1,6 +1,8 @@
//use bytesize::ByteSize; //use bytesize::ByteSize;
use format_table::format_table; use format_table::format_table;
use chrono::Local;
use garage_util::error::*; use garage_util::error::*;
use garage_api_admin::api::*; use garage_api_admin::api::*;
@@ -29,13 +31,16 @@ impl Cli {
} }
pub async fn cmd_list_buckets(&self) -> Result<(), Error> { pub async fn cmd_list_buckets(&self) -> Result<(), Error> {
let buckets = self.api_request(ListBucketsRequest).await?; let mut buckets = self.api_request(ListBucketsRequest).await?;
let mut table = vec!["ID\tGlobal aliases\tLocal aliases".to_string()]; buckets.0.sort_by_key(|x| x.created);
let mut table = vec!["ID\tCreated\tGlobal aliases\tLocal aliases".to_string()];
for bucket in buckets.0.iter() { for bucket in buckets.0.iter() {
table.push(format!( table.push(format!(
"{:.16}\t{}\t{}", "{:.16}\t{}\t{}\t{}",
bucket.id, bucket.id,
bucket.created.with_timezone(&Local).date_naive(),
table_list_abbr(&bucket.global_aliases), table_list_abbr(&bucket.global_aliases),
table_list_abbr( table_list_abbr(
bucket bucket
@@ -484,6 +489,7 @@ fn print_bucket_info(bucket: &GetBucketInfoResponse) {
let mut info = vec![ let mut info = vec![
format!("Bucket:\t{}", bucket.id), format!("Bucket:\t{}", bucket.id),
format!("Created:\t{}", bucket.created.with_timezone(&Local)),
String::new(), String::new(),
{ {
let size = bytesize::ByteSize::b(bucket.bytes as u64); let size = bytesize::ByteSize::b(bucket.bytes as u64);