Compare commits

..

46 Commits

Author SHA1 Message Date
Alex Auvolat 713c61acb8 Enable k2v feature flag in CI 2022-05-23 16:44:37 +02:00
Alex Auvolat 1c88ee9bc5 Make authorization token mandatory for admin API 2022-05-23 16:40:10 +02:00
Alex Auvolat d768f559da Update documentation with warning 2022-05-23 12:06:00 +02:00
Alex Auvolat 99976e11f8 Merge branch 'main' into admin-api 2022-05-18 22:35:49 +02:00
Alex Auvolat 5367f8adb2 Refactor bucket emptiness check and add k2v check 2022-05-18 10:09:51 +02:00
Alex Auvolat 30e393b439 Fix fmt 2022-05-18 00:32:51 +02:00
Alex Auvolat 926b3c0fad Rename error varian for Clippy 2022-05-18 00:27:57 +02:00
Alex Auvolat dcfa408887 Implement ImportKey 2022-05-17 19:02:13 +02:00
Alex Auvolat 70383b4363 Implement ConnectClusterNodes 2022-05-17 18:43:47 +02:00
Alex Auvolat 440a577563 Prefix all APIs with v0/ 2022-05-17 17:51:38 +02:00
Alex Auvolat 5072dbd228 Add PutBucketWebsite and DeleteBucketWebsite to admin api 2022-05-17 17:44:00 +02:00
Alex Auvolat 2ce3513c10 Specify and implement {Global,Local}{Alias,Unalias}Bucket 2022-05-17 17:16:29 +02:00
Alex Auvolat e92c52eb65 refactor 2022-05-17 17:02:38 +02:00
Alex Auvolat 8b1338ef2f Fix error code 2022-05-17 16:48:23 +02:00
Alex Auvolat 8ff95f09c9 Return website config in GetBucketInfo, use serde(rename_all) 2022-05-17 16:42:13 +02:00
Alex Auvolat 86a3fe8ec5 Merge branch 'main' into admin-api 2022-05-17 13:10:53 +02:00
Alex Auvolat ec50ffac42 Remove useless string conversions 2022-05-13 19:49:04 +02:00
Alex Auvolat d7736cb614 Revert useless thing 2022-05-13 19:43:40 +02:00
Alex Auvolat 8033bdb0b4 More precisions in errors & small refactoring 2022-05-13 19:36:17 +02:00
Alex Auvolat 5a535788fc Json body for custom errors 2022-05-13 19:28:23 +02:00
Alex Auvolat ea325d78d3 More error refactoring 2022-05-13 19:18:51 +02:00
Alex Auvolat ec16d166f9 Separate error types for k2v and signature 2022-05-13 15:43:44 +02:00
Alex Auvolat 7a5d329e49 More error refactoring 2022-05-13 15:21:32 +02:00
Alex Auvolat f82b938033 Rename error::Error to s3::error::Error 2022-05-13 15:10:52 +02:00
Alex Auvolat 96b11524d5 Error refactoring 2022-05-13 15:04:53 +02:00
Alex Auvolat c0fb9fd0fe Common error type and admin error type that uses it 2022-05-13 14:30:30 +02:00
Alex Auvolat 983037d965 Possibility of different error types for different APIs 2022-05-13 13:51:34 +02:00
Alex Auvolat e4e1f8f0d6 Fix clippy 2022-05-12 17:11:45 +02:00
Alex Auvolat e7ddba53e3 Slightly more detailed error reporting from helper 2022-05-12 17:10:25 +02:00
Alex Auvolat ed76893581 Simplify 2022-05-12 11:21:23 +02:00
Alex Auvolat fc2f73ddb5 BucketAllowKey and BucketDenyKey 2022-05-12 11:19:41 +02:00
Alex Auvolat fe399a3265 DeleteBucket 2022-05-12 11:02:36 +02:00
Alex Auvolat de1a5b87b6 CreateBucket 2022-05-12 10:45:09 +02:00
Alex Auvolat 2b93a01d2b ListBucket and GetBucketInfo 2022-05-12 10:20:34 +02:00
Alex Auvolat aeb978552a Short doc on UpdateKey 2022-05-11 11:51:11 +02:00
Alex Auvolat 393b76ecba Implement CreateKey, DeleteKey and rudimentary UpdateKey 2022-05-11 11:40:26 +02:00
Alex Auvolat 5c00c9fb46 First key endpoints: ListKeys and GetKeyInfo 2022-05-11 11:10:28 +02:00
Alex Auvolat f97a7845e9 Add API access key admin endpoints 2022-05-11 10:27:40 +02:00
Alex Auvolat bb6ec9ebd9 Update Cargo.nix and improve log message 2022-05-10 13:36:35 +02:00
Alex Auvolat dd54d0b2b1 Refactor code for apply/revert, implement Update/Apply/RevertLayout 2022-05-10 13:25:10 +02:00
Alex Auvolat 01c4876fb4 Specify remaining cluster-related endpoints 2022-05-10 13:25:10 +02:00
Alex Auvolat e4c61124d8 Add first draft of admin api 2022-05-10 13:25:10 +02:00
Alex Auvolat ec03e3d16c Fmt & cleanup 2022-05-10 13:25:10 +02:00
Alex Auvolat 7a19daafbd Implement /status Admin endpoint 2022-05-10 13:25:10 +02:00
Alex Auvolat 99fcfa3844 Make background runner terminate correctly 2022-05-10 13:25:10 +02:00
Alex Auvolat 633958c7b1 Refactor admin API to be in api/admin and use common code 2022-05-10 13:25:06 +02:00
8 changed files with 14 additions and 87 deletions
Generated
-1
View File
@@ -1584,7 +1584,6 @@ dependencies = [
"clap 3.1.18",
"garage_util 0.7.0",
"http",
"log",
"rusoto_core",
"rusoto_credential",
"rusoto_signature",
+2 -3
View File
@@ -688,7 +688,7 @@ in
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo { inherit name version; sha256 = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"; };
dependencies = {
${ if hostPlatform.config == "aarch64-linux-android" || hostPlatform.config == "aarch64-apple-darwin" || hostPlatform.parsed.cpu.name == "aarch64" && hostPlatform.parsed.kernel.name == "linux" then "libc" else null } = rustPackages."registry+https://github.com/rust-lang/crates.io-index".libc."0.2.121" { inherit profileName; };
${ if hostPlatform.config == "aarch64-linux-android" || hostPlatform.parsed.cpu.name == "aarch64" && hostPlatform.parsed.kernel.name == "linux" || hostPlatform.config == "aarch64-apple-darwin" then "libc" else null } = rustPackages."registry+https://github.com/rust-lang/crates.io-index".libc."0.2.121" { inherit profileName; };
};
});
@@ -2117,7 +2117,6 @@ in
clap = rustPackages."registry+https://github.com/rust-lang/crates.io-index".clap."3.1.18" { inherit profileName; };
garage_util = rustPackages."unknown".garage_util."0.7.0" { inherit profileName; };
http = rustPackages."registry+https://github.com/rust-lang/crates.io-index".http."0.2.6" { inherit profileName; };
log = rustPackages."registry+https://github.com/rust-lang/crates.io-index".log."0.4.16" { inherit profileName; };
rusoto_core = rustPackages."registry+https://github.com/rust-lang/crates.io-index".rusoto_core."0.48.0" { inherit profileName; };
rusoto_credential = rustPackages."registry+https://github.com/rust-lang/crates.io-index".rusoto_credential."0.48.0" { inherit profileName; };
rusoto_signature = rustPackages."registry+https://github.com/rust-lang/crates.io-index".rusoto_signature."0.48.0" { inherit profileName; };
@@ -5030,7 +5029,7 @@ in
[ "default" ]
];
dependencies = {
${ if hostPlatform.config == "aarch64-pc-windows-msvc" || hostPlatform.config == "aarch64-uwp-windows-msvc" then "windows_aarch64_msvc" else null } = rustPackages."registry+https://github.com/rust-lang/crates.io-index".windows_aarch64_msvc."0.32.0" { inherit profileName; };
${ if hostPlatform.config == "aarch64-uwp-windows-msvc" || hostPlatform.config == "aarch64-pc-windows-msvc" then "windows_aarch64_msvc" else null } = rustPackages."registry+https://github.com/rust-lang/crates.io-index".windows_aarch64_msvc."0.32.0" { inherit profileName; };
${ if hostPlatform.config == "i686-pc-windows-gnu" || hostPlatform.config == "i686-uwp-windows-gnu" then "windows_i686_gnu" else null } = rustPackages."registry+https://github.com/rust-lang/crates.io-index".windows_i686_gnu."0.32.0" { inherit profileName; };
${ if hostPlatform.config == "i686-uwp-windows-msvc" || hostPlatform.config == "i686-pc-windows-msvc" then "windows_i686_msvc" else null } = rustPackages."registry+https://github.com/rust-lang/crates.io-index".windows_i686_msvc."0.32.0" { inherit profileName; };
${ if hostPlatform.config == "x86_64-pc-windows-gnu" || hostPlatform.config == "x86_64-uwp-windows-gnu" then "windows_x86_64_gnu" else null } = rustPackages."registry+https://github.com/rust-lang/crates.io-index".windows_x86_64_gnu."0.32.0" { inherit profileName; };
+5 -12
View File
@@ -12,7 +12,7 @@ when changes are introduced.
The admin API uses two different tokens for acces control, that are specified in the config file's `[admin]` section:
- `metrics_token`: the token for accessing the Metrics endpoint (if this token is not set in the config file, the Metrics endpoint can be accessed without access control);
- `admin_token`: the token for accessing all of the other administration endpoints (if this token is not set in the config file, access to these endpoints is disabled entirely).
- `admin_token`: the token for accessing all of the other administration endpoints (if this token is not set in the config file, these endpoints can be accessed without access control).
## Administration API endpoints
@@ -124,11 +124,11 @@ Example response:
[
{
"success": true,
"error": null
"error": null,
},
{
"success": false,
"error": "Handshake error"
"error": "Handshake error",
}
]
```
@@ -490,11 +490,7 @@ OR
"localAlias": {
"accessKeyId": "GK31c2f218a2e44f485b94239e",
"alias": "NameOfMyBucket",
"allow": {
"read": true,
"write": true,
"owner": false
}
"allPermissions": true
}
}
```
@@ -508,9 +504,6 @@ OR
Creates a new bucket, either with a global alias, a local one,
or no alias at all.
Technically, you can also specify both `globalAlias` and `localAlias` and that would create
two aliases, but I don't see why you would want to do that.
#### DeleteBucket `DELETE /v0/bucket?id=<bucket id>`
Deletes a storage bucket. A bucket cannot be deleted if it is not empty.
@@ -526,7 +519,7 @@ Request body format:
```json
{
"indexDocument": "index.html",
"errorDocument": "404.html"
"errorDocument": "404.html",
}
```
+3 -10
View File
@@ -7,7 +7,6 @@ use serde::{Deserialize, Serialize};
use garage_util::crdt::*;
use garage_util::data::*;
use garage_util::error::Error as GarageError;
use garage_util::time::*;
use garage_table::*;
@@ -284,19 +283,13 @@ pub async fn handle_create_bucket(
.bucket_helper()
.set_local_bucket_alias(bucket.id, &la.access_key_id, &la.alias)
.await?;
if la.allow.read || la.allow.write || la.allow.owner {
if la.all_permissions {
garage
.bucket_helper()
.set_bucket_key_permissions(
bucket.id,
&la.access_key_id,
BucketKeyPerm {
timestamp: now_msec(),
allow_read: la.allow.read,
allow_write: la.allow.write,
allow_owner: la.allow.owner,
},
BucketKeyPerm::ALL_PERMISSIONS,
)
.await?;
}
@@ -318,7 +311,7 @@ struct CreateBucketLocalAlias {
access_key_id: String,
alias: String,
#[serde(default)]
allow: ApiBucketKeyPerm,
all_permissions: bool,
}
pub async fn handle_delete_bucket(
-5
View File
@@ -6,7 +6,6 @@ edition = "2018"
[dependencies]
base64 = "0.13.0"
http = "0.2.6"
log = "0.4"
rusoto_core = "0.48.0"
rusoto_credential = "0.48.0"
rusoto_signature = "0.48.0"
@@ -23,10 +22,6 @@ garage_util = { path = "../util", optional = true }
[features]
cli = ["clap", "tokio/fs", "tokio/io-std", "garage_util"]
[lib]
path = "lib.rs"
[[bin]]
name = "k2v-cli"
path = "bin/k2v-cli.rs"
required-features = ["cli"]
@@ -5,13 +5,6 @@ use thiserror::Error;
/// Errors returned by this crate
#[derive(Error, Debug)]
pub enum Error {
#[error("{0}, {1}: {2} (path = {3})")]
Remote(
http::StatusCode,
Cow<'static, str>,
Cow<'static, str>,
Cow<'static, str>,
),
#[error("received invalid response: {0}")]
InvalidResponse(Cow<'static, str>),
#[error("not found")]
@@ -4,7 +4,6 @@ use std::time::Duration;
use http::header::{ACCEPT, CONTENT_LENGTH, CONTENT_TYPE};
use http::status::StatusCode;
use http::HeaderMap;
use log::{debug, error};
use rusoto_core::{ByteStream, DispatchSignedRequest, HttpClient};
use rusoto_credential::AwsCredentials;
@@ -311,47 +310,12 @@ impl K2vClient {
StatusCode::NO_CONTENT => Vec::new(),
StatusCode::NOT_FOUND => return Err(Error::NotFound),
StatusCode::NOT_MODIFIED => Vec::new(),
s => {
let err_body = read_body(&mut res.headers, res.body)
.await
.unwrap_or_default();
let err_body_str = std::str::from_utf8(&err_body)
.map(String::from)
.unwrap_or_else(|_| base64::encode(&err_body));
if s.is_client_error() || s.is_server_error() {
error!("Error response {}: {}", res.status, err_body_str);
let err = match serde_json::from_slice::<ErrorResponse>(&err_body) {
Ok(err) => Error::Remote(
res.status,
err.code.into(),
err.message.into(),
err.path.into(),
),
Err(_) => Error::Remote(
res.status,
"unknown".into(),
err_body_str.into(),
"?".into(),
),
};
return Err(err);
} else {
let msg = format!(
"Unexpected response code {}. Response body: {}",
res.status, err_body_str
);
error!("{}", msg);
return Err(Error::InvalidResponse(msg.into()));
}
_ => {
return Err(Error::InvalidResponse(
format!("invalid error code: {}", res.status).into(),
))
}
};
debug!(
"Response body: {}",
std::str::from_utf8(&body)
.map(String::from)
.unwrap_or_else(|_| base64::encode(&body))
);
Ok(Response {
body,
@@ -594,15 +558,6 @@ struct BatchDeleteResponse<'a> {
deleted_items: u64,
}
#[derive(Deserialize)]
struct ErrorResponse {
code: String,
message: String,
#[allow(dead_code)]
region: String,
path: String,
}
struct Response {
body: Vec<u8>,
status: StatusCode,