mirror of
https://git.deuxfleurs.fr/Deuxfleurs/garage.git
synced 2025-03-28 04:35:28 +00:00
cli: add garage json-api
command and fix cargo tests
This commit is contained in:
parent
e6862c5d3d
commit
f7d9c2b383
4 changed files with 64 additions and 16 deletions
|
@ -50,6 +50,7 @@ sodiumoxide.workspace = true
|
|||
structopt.workspace = true
|
||||
git-version.workspace = true
|
||||
utoipa.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
futures.workspace = true
|
||||
tokio.workspace = true
|
||||
|
|
|
@ -43,6 +43,7 @@ impl Cli {
|
|||
Command::Meta(mo) => self.cmd_meta(mo).await,
|
||||
Command::Stats(so) => self.cmd_stats(so).await,
|
||||
Command::Repair(ro) => self.cmd_repair(ro).await,
|
||||
Command::JsonApi { endpoint, payload } => self.cmd_json_api(endpoint, payload).await,
|
||||
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
@ -105,6 +106,49 @@ impl Cli {
|
|||
}
|
||||
Ok(resp.success.into_iter().next().unwrap().1)
|
||||
}
|
||||
|
||||
pub async fn cmd_json_api(&self, endpoint: String, payload: String) -> Result<(), Error> {
|
||||
let payload: serde_json::Value = if payload == "-" {
|
||||
serde_json::from_reader(&std::io::stdin())?
|
||||
} else {
|
||||
serde_json::from_str(&payload)?
|
||||
};
|
||||
|
||||
let request: AdminApiRequest = serde_json::from_value(serde_json::json!({
|
||||
endpoint.clone(): payload,
|
||||
}))?;
|
||||
|
||||
let resp = match self
|
||||
.proxy_rpc_endpoint
|
||||
.call(&self.rpc_host, ProxyRpc::Proxy(request), PRIO_NORMAL)
|
||||
.await??
|
||||
{
|
||||
ProxyRpcResponse::ProxyApiOkResponse(resp) => resp,
|
||||
ProxyRpcResponse::ApiErrorResponse {
|
||||
http_code,
|
||||
error_code,
|
||||
message,
|
||||
} => {
|
||||
return Err(Error::Message(format!(
|
||||
"{} ({}): {}",
|
||||
error_code, http_code, message
|
||||
)))
|
||||
}
|
||||
m => return Err(Error::unexpected_rpc_message(m)),
|
||||
};
|
||||
|
||||
if let serde_json::Value::Object(map) = serde_json::to_value(&resp)? {
|
||||
if let Some(inner) = map.get(&endpoint) {
|
||||
serde_json::to_writer_pretty(std::io::stdout(), &inner)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::Message(format!(
|
||||
"Invalid response: {}",
|
||||
serde_json::to_string(&resp)?
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn table_list_abbr<T: IntoIterator<Item = S>, S: AsRef<str>>(values: T) -> String {
|
||||
|
|
|
@ -66,6 +66,17 @@ pub enum Command {
|
|||
/// Output openapi JSON schema for admin api
|
||||
#[structopt(name = "admin-api-schema", version = garage_version(), setting(structopt::clap::AppSettings::Hidden))]
|
||||
AdminApiSchema,
|
||||
|
||||
/// Directly invoke the admin API using a JSON payload.
|
||||
/// The result is printed to `stdout` in JSON format.
|
||||
#[structopt(name = "json-api", version = garage_version())]
|
||||
JsonApi {
|
||||
/// The admin API endpoint to invoke, e.g. GetClusterStatus
|
||||
endpoint: String,
|
||||
/// The JSON payload, or `-` to read from `stdin`
|
||||
#[structopt(default_value = "null")]
|
||||
payload: String,
|
||||
},
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
|
|
|
@ -3,6 +3,8 @@ use std::path::{Path, PathBuf};
|
|||
use std::process;
|
||||
use std::sync::Once;
|
||||
|
||||
use serde_json::json;
|
||||
|
||||
use super::ext::*;
|
||||
|
||||
// https://xkcd.com/221/
|
||||
|
@ -193,27 +195,17 @@ api_bind_addr = "127.0.0.1:{admin_port}"
|
|||
let mut key = Key::default();
|
||||
|
||||
let mut cmd = self.command();
|
||||
let base = cmd.args(["key", "create"]);
|
||||
let base = cmd.args(["json-api", "CreateKey"]);
|
||||
let with_name = match maybe_name {
|
||||
Some(name) => base.args([name]),
|
||||
None => base,
|
||||
Some(name) => base.args([serde_json::to_string(&json!({"name": name})).unwrap()]),
|
||||
None => base.args(["{}"]),
|
||||
};
|
||||
|
||||
let output = with_name.expect_success_output("Could not create key");
|
||||
let stdout = String::from_utf8(output.stdout).unwrap();
|
||||
let stdout: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
|
||||
|
||||
for line in stdout.lines() {
|
||||
if let Some(key_id) = line.strip_prefix("Key ID: ") {
|
||||
key.id = key_id.to_owned();
|
||||
continue;
|
||||
}
|
||||
if let Some(key_secret) = line.strip_prefix("Secret key: ") {
|
||||
key.secret = key_secret.to_owned();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
assert!(!key.id.is_empty(), "Invalid key: Key ID is empty");
|
||||
assert!(!key.secret.is_empty(), "Invalid key: Key secret is empty");
|
||||
key.id = stdout["accessKeyId"].as_str().unwrap().to_string();
|
||||
key.secret = stdout["secretAccessKey"].as_str().unwrap().to_string();
|
||||
|
||||
key
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue