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
9 changed files with 17 additions and 89 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; };
+3 -2
View File
@@ -86,13 +86,14 @@ in let
It speeds up the compilation (when the feature is not required) and released crates have less dependency by default (less attack surface, disk space, etc.).
But we want to ship these additional features when we release Garage.
In the end, we chose to exclude all features from debug builds while putting (all of) them in the release builds.
Currently, the only feature of Garage is kubernetes-discovery from the garage_rpc crate.
Currently, the only such feature of Garage is kubernetes-discovery from the garage_rpc crate.
The experimental feature k2v is also enabled here in all builds.
*/
(pkgs.rustBuilder.rustLib.makeOverride {
name = "garage_rpc";
overrideArgs = old:
{
features = if release then [ "kubernetes-discovery" ] else [];
features = if release then [ "kubernetes-discovery" "k2v" ] else [ "k2v" ];
};
})
];
+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,