diff options
author | Alex Auvolat <lx@deuxfleurs.fr> | 2025-02-14 13:03:11 +0100 |
---|---|---|
committer | Alex Auvolat <lx@deuxfleurs.fr> | 2025-02-14 13:11:33 +0100 |
commit | 315169501173c1cf5f78397375fe05ea2ebb77f0 (patch) | |
tree | 777b81f299208afb795b84e9626863b8bbaddd81 /src/api/admin/api.rs | |
parent | f034e834fa70f579bfd85745aea533b4328cbce4 (diff) | |
parent | 61f3de649646d098812e6ddb814e20ce7e66ad94 (diff) | |
download | garage-315169501173c1cf5f78397375fe05ea2ebb77f0.tar.gz garage-315169501173c1cf5f78397375fe05ea2ebb77f0.zip |
Merge branch 'next-v2' into 1686a/s3-redirects
Diffstat (limited to 'src/api/admin/api.rs')
-rw-r--r-- | src/api/admin/api.rs | 876 |
1 files changed, 876 insertions, 0 deletions
diff --git a/src/api/admin/api.rs b/src/api/admin/api.rs new file mode 100644 index 00000000..97cde158 --- /dev/null +++ b/src/api/admin/api.rs @@ -0,0 +1,876 @@ +use std::collections::HashMap; +use std::convert::TryFrom; +use std::net::SocketAddr; +use std::sync::Arc; + +use paste::paste; +use serde::{Deserialize, Serialize}; + +use garage_rpc::*; + +use garage_model::garage::Garage; + +use garage_api_common::common_error::CommonErrorDerivative; +use garage_api_common::helpers::is_default; + +use crate::api_server::{AdminRpc, AdminRpcResponse}; +use crate::error::Error; +use crate::macros::*; +use crate::{Admin, RequestHandler}; + +// This generates the following: +// +// - An enum AdminApiRequest that contains a variant for all endpoints +// +// - An enum AdminApiResponse that contains a variant for all non-special endpoints. +// This enum is serialized in api_server.rs, without the enum tag, +// which gives directly the JSON response corresponding to the API call. +// This enum does not implement Deserialize as its meaning can be ambiguous. +// +// - An enum TaggedAdminApiResponse that contains the same variants, but +// serializes as a tagged enum. This allows it to be transmitted through +// Garage RPC and deserialized correctly upon receival. +// Conversion from untagged to tagged can be done using the `.tagged()` method. +// +// - AdminApiRequest::name() that returns the name of the endpoint +// +// - impl EndpointHandler for AdminApiHandler, that uses the impl EndpointHandler +// of each request type below for non-special endpoints +admin_endpoints![ + // Special endpoints of the Admin API + @special Options, + @special CheckDomain, + @special Health, + @special Metrics, + + // Cluster operations + GetClusterStatus, + GetClusterHealth, + ConnectClusterNodes, + GetClusterLayout, + UpdateClusterLayout, + ApplyClusterLayout, + RevertClusterLayout, + + // Access key operations + ListKeys, + GetKeyInfo, + CreateKey, + ImportKey, + UpdateKey, + DeleteKey, + + // Bucket operations + ListBuckets, + GetBucketInfo, + CreateBucket, + UpdateBucket, + DeleteBucket, + CleanupIncompleteUploads, + + // Operations on permissions for keys on buckets + AllowBucketKey, + DenyBucketKey, + + // Operations on bucket aliases + AddBucketAlias, + RemoveBucketAlias, + + // Node operations + CreateMetadataSnapshot, + GetNodeStatistics, + GetClusterStatistics, + LaunchRepairOperation, + + // Worker operations + ListWorkers, + GetWorkerInfo, + GetWorkerVariable, + SetWorkerVariable, + + // Block operations + ListBlockErrors, + GetBlockInfo, + RetryBlockResync, + PurgeBlocks, +]; + +local_admin_endpoints![ + // Node operations + CreateMetadataSnapshot, + GetNodeStatistics, + LaunchRepairOperation, + // Background workers + ListWorkers, + GetWorkerInfo, + GetWorkerVariable, + SetWorkerVariable, + // Block operations + ListBlockErrors, + GetBlockInfo, + RetryBlockResync, + PurgeBlocks, +]; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MultiRequest<RB> { + pub node: String, + pub body: RB, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MultiResponse<RB> { + pub success: HashMap<String, RB>, + pub error: HashMap<String, String>, +} + +// ********************************************** +// Special endpoints +// +// These endpoints don't have associated *Response structs +// because they directly produce an http::Response +// ********************************************** + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct OptionsRequest; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CheckDomainRequest { + pub domain: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HealthRequest; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MetricsRequest; + +// ********************************************** +// Cluster operations +// ********************************************** + +// ---- GetClusterStatus ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GetClusterStatusRequest; + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetClusterStatusResponse { + pub node: String, + pub garage_version: String, + pub garage_features: Option<Vec<String>>, + pub rust_version: String, + pub db_engine: String, + pub layout_version: u64, + pub nodes: Vec<NodeResp>, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[serde(rename_all = "camelCase")] +pub struct NodeResp { + pub id: String, + pub role: Option<NodeRoleResp>, + pub addr: Option<SocketAddr>, + pub hostname: Option<String>, + pub is_up: bool, + pub last_seen_secs_ago: Option<u64>, + pub draining: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub data_partition: Option<FreeSpaceResp>, + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata_partition: Option<FreeSpaceResp>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NodeRoleResp { + pub id: String, + pub zone: String, + pub capacity: Option<u64>, + pub tags: Vec<String>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct FreeSpaceResp { + pub available: u64, + pub total: u64, +} + +// ---- GetClusterHealth ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GetClusterHealthRequest; + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetClusterHealthResponse { + pub status: String, + pub known_nodes: usize, + pub connected_nodes: usize, + pub storage_nodes: usize, + pub storage_nodes_ok: usize, + pub partitions: usize, + pub partitions_quorum: usize, + pub partitions_all_ok: usize, +} + +// ---- ConnectClusterNodes ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ConnectClusterNodesRequest(pub Vec<String>); + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ConnectClusterNodesResponse(pub Vec<ConnectNodeResponse>); + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ConnectNodeResponse { + pub success: bool, + pub error: Option<String>, +} + +// ---- GetClusterLayout ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GetClusterLayoutRequest; + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetClusterLayoutResponse { + pub version: u64, + pub roles: Vec<NodeRoleResp>, + pub staged_role_changes: Vec<NodeRoleChange>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NodeRoleChange { + pub id: String, + #[serde(flatten)] + pub action: NodeRoleChangeEnum, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum NodeRoleChangeEnum { + #[serde(rename_all = "camelCase")] + Remove { remove: bool }, + #[serde(rename_all = "camelCase")] + Update { + zone: String, + capacity: Option<u64>, + tags: Vec<String>, + }, +} + +// ---- UpdateClusterLayout ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UpdateClusterLayoutRequest(pub Vec<NodeRoleChange>); + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UpdateClusterLayoutResponse(pub GetClusterLayoutResponse); + +// ---- ApplyClusterLayout ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ApplyClusterLayoutRequest { + pub version: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ApplyClusterLayoutResponse { + pub message: Vec<String>, + pub layout: GetClusterLayoutResponse, +} + +// ---- RevertClusterLayout ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RevertClusterLayoutRequest; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RevertClusterLayoutResponse(pub GetClusterLayoutResponse); + +// ********************************************** +// Access key operations +// ********************************************** + +// ---- ListKeys ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ListKeysRequest; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ListKeysResponse(pub Vec<ListKeysResponseItem>); + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ListKeysResponseItem { + pub id: String, + pub name: String, +} + +// ---- GetKeyInfo ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GetKeyInfoRequest { + pub id: Option<String>, + pub search: Option<String>, + pub show_secret_key: bool, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetKeyInfoResponse { + pub name: String, + pub access_key_id: String, + #[serde(skip_serializing_if = "is_default")] + pub secret_access_key: Option<String>, + pub permissions: KeyPerm, + pub buckets: Vec<KeyInfoBucketResponse>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct KeyPerm { + #[serde(default)] + pub create_bucket: bool, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct KeyInfoBucketResponse { + pub id: String, + pub global_aliases: Vec<String>, + pub local_aliases: Vec<String>, + pub permissions: ApiBucketKeyPerm, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[serde(rename_all = "camelCase")] +pub struct ApiBucketKeyPerm { + #[serde(default)] + pub read: bool, + #[serde(default)] + pub write: bool, + #[serde(default)] + pub owner: bool, +} + +// ---- CreateKey ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CreateKeyRequest { + pub name: Option<String>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CreateKeyResponse(pub GetKeyInfoResponse); + +// ---- ImportKey ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ImportKeyRequest { + pub access_key_id: String, + pub secret_access_key: String, + pub name: Option<String>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ImportKeyResponse(pub GetKeyInfoResponse); + +// ---- UpdateKey ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UpdateKeyRequest { + pub id: String, + pub body: UpdateKeyRequestBody, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UpdateKeyResponse(pub GetKeyInfoResponse); + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UpdateKeyRequestBody { + pub name: Option<String>, + pub allow: Option<KeyPerm>, + pub deny: Option<KeyPerm>, +} + +// ---- DeleteKey ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DeleteKeyRequest { + pub id: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DeleteKeyResponse; + +// ********************************************** +// Bucket operations +// ********************************************** + +// ---- ListBuckets ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ListBucketsRequest; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ListBucketsResponse(pub Vec<ListBucketsResponseItem>); + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ListBucketsResponseItem { + pub id: String, + pub global_aliases: Vec<String>, + pub local_aliases: Vec<BucketLocalAlias>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct BucketLocalAlias { + pub access_key_id: String, + pub alias: String, +} + +// ---- GetBucketInfo ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GetBucketInfoRequest { + pub id: Option<String>, + pub global_alias: Option<String>, + pub search: Option<String>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetBucketInfoResponse { + pub id: String, + pub global_aliases: Vec<String>, + pub website_access: bool, + #[serde(default)] + pub website_config: Option<GetBucketInfoWebsiteResponse>, + pub keys: Vec<GetBucketInfoKey>, + pub objects: i64, + pub bytes: i64, + pub unfinished_uploads: i64, + pub unfinished_multipart_uploads: i64, + pub unfinished_multipart_upload_parts: i64, + pub unfinished_multipart_upload_bytes: i64, + pub quotas: ApiBucketQuotas, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetBucketInfoWebsiteResponse { + pub index_document: String, + pub error_document: Option<String>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GetBucketInfoKey { + pub access_key_id: String, + pub name: String, + pub permissions: ApiBucketKeyPerm, + pub bucket_local_aliases: Vec<String>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ApiBucketQuotas { + pub max_size: Option<u64>, + pub max_objects: Option<u64>, +} + +// ---- CreateBucket ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CreateBucketRequest { + pub global_alias: Option<String>, + pub local_alias: Option<CreateBucketLocalAlias>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CreateBucketResponse(pub GetBucketInfoResponse); + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CreateBucketLocalAlias { + pub access_key_id: String, + pub alias: String, + #[serde(default)] + pub allow: ApiBucketKeyPerm, +} + +// ---- UpdateBucket ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UpdateBucketRequest { + pub id: String, + pub body: UpdateBucketRequestBody, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UpdateBucketResponse(pub GetBucketInfoResponse); + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UpdateBucketRequestBody { + pub website_access: Option<UpdateBucketWebsiteAccess>, + pub quotas: Option<ApiBucketQuotas>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UpdateBucketWebsiteAccess { + pub enabled: bool, + pub index_document: Option<String>, + pub error_document: Option<String>, +} + +// ---- DeleteBucket ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DeleteBucketRequest { + pub id: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DeleteBucketResponse; + +// ---- CleanupIncompleteUploads ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CleanupIncompleteUploadsRequest { + pub bucket_id: String, + pub older_than_secs: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CleanupIncompleteUploadsResponse { + pub uploads_deleted: u64, +} + +// ********************************************** +// Operations on permissions for keys on buckets +// ********************************************** + +// ---- AllowBucketKey ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AllowBucketKeyRequest(pub BucketKeyPermChangeRequest); + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AllowBucketKeyResponse(pub GetBucketInfoResponse); + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct BucketKeyPermChangeRequest { + pub bucket_id: String, + pub access_key_id: String, + pub permissions: ApiBucketKeyPerm, +} + +// ---- DenyBucketKey ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DenyBucketKeyRequest(pub BucketKeyPermChangeRequest); + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DenyBucketKeyResponse(pub GetBucketInfoResponse); + +// ********************************************** +// Operations on bucket aliases +// ********************************************** + +// ---- AddBucketAlias ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AddBucketAliasRequest { + pub bucket_id: String, + #[serde(flatten)] + pub alias: BucketAliasEnum, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AddBucketAliasResponse(pub GetBucketInfoResponse); + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum BucketAliasEnum { + #[serde(rename_all = "camelCase")] + Global { global_alias: String }, + #[serde(rename_all = "camelCase")] + Local { + local_alias: String, + access_key_id: String, + }, +} + +// ---- RemoveBucketAlias ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RemoveBucketAliasRequest { + pub bucket_id: String, + #[serde(flatten)] + pub alias: BucketAliasEnum, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RemoveBucketAliasResponse(pub GetBucketInfoResponse); + +// ********************************************** +// Node operations +// ********************************************** + +// ---- CreateMetadataSnapshot ---- + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct LocalCreateMetadataSnapshotRequest; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LocalCreateMetadataSnapshotResponse; + +// ---- GetNodeStatistics ---- + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct LocalGetNodeStatisticsRequest; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LocalGetNodeStatisticsResponse { + pub freeform: String, +} + +// ---- GetClusterStatistics ---- + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct GetClusterStatisticsRequest; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GetClusterStatisticsResponse { + pub freeform: String, +} + +// ---- LaunchRepairOperation ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LocalLaunchRepairOperationRequest { + pub repair_type: RepairType, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum RepairType { + Tables, + Blocks, + Versions, + MultipartUploads, + BlockRefs, + BlockRc, + Rebalance, + Scrub(ScrubCommand), +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum ScrubCommand { + Start, + Pause, + Resume, + Cancel, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LocalLaunchRepairOperationResponse; + +// ********************************************** +// Worker operations +// ********************************************** + +// ---- GetWorkerList ---- + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[serde(rename_all = "camelCase")] +pub struct LocalListWorkersRequest { + #[serde(default)] + pub busy_only: bool, + #[serde(default)] + pub error_only: bool, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LocalListWorkersResponse(pub Vec<WorkerInfoResp>); + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct WorkerInfoResp { + pub id: u64, + pub name: String, + pub state: WorkerStateResp, + pub errors: u64, + pub consecutive_errors: u64, + pub last_error: Option<WorkerLastError>, + pub tranquility: Option<u32>, + pub progress: Option<String>, + pub queue_length: Option<u64>, + pub persistent_errors: Option<u64>, + pub freeform: Vec<String>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum WorkerStateResp { + Busy, + Throttled { duration_secs: f32 }, + Idle, + Done, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct WorkerLastError { + pub message: String, + pub secs_ago: u64, +} + +// ---- GetWorkerList ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LocalGetWorkerInfoRequest { + pub id: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LocalGetWorkerInfoResponse(pub WorkerInfoResp); + +// ---- GetWorkerVariable ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LocalGetWorkerVariableRequest { + pub variable: Option<String>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LocalGetWorkerVariableResponse(pub HashMap<String, String>); + +// ---- SetWorkerVariable ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LocalSetWorkerVariableRequest { + pub variable: String, + pub value: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LocalSetWorkerVariableResponse { + pub variable: String, + pub value: String, +} + +// ********************************************** +// Block operations +// ********************************************** + +// ---- ListBlockErrors ---- + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct LocalListBlockErrorsRequest; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LocalListBlockErrorsResponse(pub Vec<BlockError>); + +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(rename_all = "camelCase")] +pub struct BlockError { + pub block_hash: String, + pub refcount: u64, + pub error_count: u64, + pub last_try_secs_ago: u64, + pub next_try_in_secs: u64, +} + +// ---- GetBlockInfo ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LocalGetBlockInfoRequest { + pub block_hash: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LocalGetBlockInfoResponse { + pub block_hash: String, + pub refcount: u64, + pub versions: Vec<BlockVersion>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct BlockVersion { + pub version_id: String, + pub deleted: bool, + pub garbage_collected: bool, + pub backlink: Option<BlockVersionBacklink>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum BlockVersionBacklink { + Object { + bucket_id: String, + key: String, + }, + Upload { + upload_id: String, + upload_deleted: bool, + upload_garbage_collected: bool, + bucket_id: Option<String>, + key: Option<String>, + }, +} + +// ---- RetryBlockResync ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum LocalRetryBlockResyncRequest { + #[serde(rename_all = "camelCase")] + All { all: bool }, + #[serde(rename_all = "camelCase")] + Blocks { block_hashes: Vec<String> }, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LocalRetryBlockResyncResponse { + pub count: u64, +} + +// ---- PurgeBlocks ---- + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LocalPurgeBlocksRequest(pub Vec<String>); + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LocalPurgeBlocksResponse { + pub blocks_purged: u64, + pub objects_deleted: u64, + pub uploads_deleted: u64, + pub versions_deleted: u64, +} |