file-restore: add 'timeout' and 'json-error' parameter
timeout limits the code with the given timeout in seconds, and 'json-error' return json to stdout when the call returns an error like this: { "msg": "error message", "error": true, "code": <HTTP_STATUS_CODE>, // if it was an http error } with both options set, a client can more easily determine if the call ran into a timeout (since it will return a 503 error), and can poll it again both is done behind new parameters, so that we can stay backwards-compatible Signed-off-by: Dominik Csapak <d.csapak@proxmox.com> Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
parent
25be1fa0d7
commit
6ddd69c5ce
|
@ -11,6 +11,7 @@ use proxmox_router::cli::{
|
||||||
get_output_format, run_cli_command, CliCommand, CliCommandMap, CliEnvironment, ColumnConfig,
|
get_output_format, run_cli_command, CliCommand, CliCommandMap, CliEnvironment, ColumnConfig,
|
||||||
OUTPUT_FORMAT,
|
OUTPUT_FORMAT,
|
||||||
};
|
};
|
||||||
|
use proxmox_router::{http_err, HttpError};
|
||||||
use proxmox_schema::api;
|
use proxmox_schema::api;
|
||||||
use proxmox_sys::fs::{create_path, CreateOptions};
|
use proxmox_sys::fs::{create_path, CreateOptions};
|
||||||
use pxar::accessor::aio::Accessor;
|
use pxar::accessor::aio::Accessor;
|
||||||
|
@ -211,6 +212,18 @@ async fn list_files(
|
||||||
schema: OUTPUT_FORMAT,
|
schema: OUTPUT_FORMAT,
|
||||||
optional: true,
|
optional: true,
|
||||||
},
|
},
|
||||||
|
"json-error": {
|
||||||
|
type: Boolean,
|
||||||
|
description: "If set, errors are returned as json instead of writing to stderr",
|
||||||
|
optional: true,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
"timeout": {
|
||||||
|
type: Integer,
|
||||||
|
description: "Defines the maximum time the call can should take.",
|
||||||
|
minimum: 1,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
returns: {
|
returns: {
|
||||||
|
@ -222,7 +235,14 @@ async fn list_files(
|
||||||
}
|
}
|
||||||
)]
|
)]
|
||||||
/// List a directory from a backup snapshot.
|
/// List a directory from a backup snapshot.
|
||||||
async fn list(snapshot: String, path: String, base64: bool, param: Value) -> Result<(), Error> {
|
async fn list(
|
||||||
|
snapshot: String,
|
||||||
|
path: String,
|
||||||
|
base64: bool,
|
||||||
|
json_error: bool,
|
||||||
|
timeout: Option<u64>,
|
||||||
|
param: Value,
|
||||||
|
) -> Result<(), Error> {
|
||||||
let repo = extract_repository_from_value(¶m)?;
|
let repo = extract_repository_from_value(¶m)?;
|
||||||
let snapshot: BackupDir = snapshot.parse()?;
|
let snapshot: BackupDir = snapshot.parse()?;
|
||||||
let path = parse_path(path, base64)?;
|
let path = parse_path(path, base64)?;
|
||||||
|
@ -246,7 +266,43 @@ async fn list(snapshot: String, path: String, base64: bool, param: Value) -> Res
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = list_files(repo, snapshot, path, crypt_config, keyfile, driver).await?;
|
let result = if let Some(timeout) = timeout {
|
||||||
|
match tokio::time::timeout(
|
||||||
|
std::time::Duration::from_secs(timeout),
|
||||||
|
list_files(repo, snapshot, path, crypt_config, keyfile, driver),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(_) => Err(http_err!(SERVICE_UNAVAILABLE, "list not finished in time")),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
list_files(repo, snapshot, path, crypt_config, keyfile, driver).await
|
||||||
|
};
|
||||||
|
|
||||||
|
let output_format = get_output_format(¶m);
|
||||||
|
|
||||||
|
if let Err(err) = result {
|
||||||
|
if !json_error {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
let (msg, code) = match err.downcast_ref::<HttpError>() {
|
||||||
|
Some(HttpError { code, message }) => (message.clone(), Some(code)),
|
||||||
|
None => (err.to_string(), None),
|
||||||
|
};
|
||||||
|
let mut json_err = json!({
|
||||||
|
"error": true,
|
||||||
|
"message": msg,
|
||||||
|
});
|
||||||
|
if let Some(code) = code {
|
||||||
|
json_err["code"] = Value::from(code.as_u16());
|
||||||
|
}
|
||||||
|
match output_format.as_ref() {
|
||||||
|
"json-pretty" => println!("{}", serde_json::to_string_pretty(&json_err)?),
|
||||||
|
_ => println!("{}", serde_json::to_string(&json_err)?),
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
let options = default_table_format_options()
|
let options = default_table_format_options()
|
||||||
.sortby("type", false)
|
.sortby("type", false)
|
||||||
|
@ -258,7 +314,7 @@ async fn list(snapshot: String, path: String, base64: bool, param: Value) -> Res
|
||||||
|
|
||||||
let output_format = get_output_format(¶m);
|
let output_format = get_output_format(¶m);
|
||||||
format_and_print_result_full(
|
format_and_print_result_full(
|
||||||
&mut json!(result),
|
&mut json!(result.unwrap()),
|
||||||
&API_METHOD_LIST.returns,
|
&API_METHOD_LIST.returns,
|
||||||
&output_format,
|
&output_format,
|
||||||
&options,
|
&options,
|
||||||
|
|
Loading…
Reference in New Issue