tools/unitctl: unitctl export

* new subcommand for "export" in CLI
* new cmd submodule for exporting config tarballs
* logic to also output to stdout
* README additions
* limitations documented

Signed-off-by: Ava Hahn <a.hahn@f5.com>
This commit is contained in:
Ava Hahn 2024-06-18 23:06:05 -07:00 committed by Ava Hahn
parent e0c15ae457
commit 57a0f94efb
7 changed files with 130 additions and 22 deletions

View file

@ -485,23 +485,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.1"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"errno-dragonfly",
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
"windows-sys 0.52.0",
]
[[package]]
@ -510,6 +499,18 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "filetime"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"windows-sys 0.52.0",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -1009,9 +1010,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.150"
version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "libloading"
@ -1034,9 +1035,9 @@ dependencies = [
[[package]]
name = "linux-raw-sys"
version = "0.4.11"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "log"
@ -1488,15 +1489,15 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustix"
version = "0.38.25"
version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags 2.4.1",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.48.0",
"windows-sys 0.52.0",
]
[[package]]
@ -1788,6 +1789,17 @@ dependencies = [
"windows",
]
[[package]]
name = "tar"
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909"
dependencies = [
"filetime",
"libc",
"xattr",
]
[[package]]
name = "tempfile"
version = "3.8.1"
@ -2061,6 +2073,7 @@ dependencies = [
"serde",
"serde_json",
"serde_yaml",
"tar",
"tempfile",
"tokio",
"unit-client-rs",
@ -2439,6 +2452,17 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "xattr"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f"
dependencies = [
"libc",
"linux-raw-sys",
"rustix",
]
[[package]]
name = "yansi"
version = "0.5.1"

View file

@ -177,6 +177,20 @@ Imported /opt/unit/config/put.json -> /config
Imported 3 files
```
### Export configuration from a running Unit instance
```
$ unitctl export -f config.tar
```
Addtionally, standard out can be used:
```
$ unitctl export -f -
$ unitctl export -f - | tar xf - config.json
$ unitctl export -f - > config.tar
```
*Note:* The exported configuration omits certificates.
### Wait for socket to become available
```
$ unitctl --wait-timeout-seconds=3 --wait-max-tries=4 import /opt/unit/config`

View file

@ -32,6 +32,7 @@ hyperlocal = "0.8"
hyper-tls = "0.5"
tokio = { version = "1.35", features = ["macros"] }
futures = "0.3"
tar = "0.4.41"
[package.metadata.deb]
copyright = "2022, F5"

View file

@ -5,3 +5,4 @@ pub(crate) mod import;
pub(crate) mod instances;
pub(crate) mod listeners;
pub(crate) mod status;
pub(crate) mod save;

View file

@ -0,0 +1,52 @@
use crate::unitctl::UnitCtl;
use crate::wait;
use crate::UnitctlError;
use crate::requests::send_empty_body_deserialize_response;
use unit_client_rs::unit_client::UnitClient;
use tar::{Builder, Header};
use std::fs::File;
use std::io::stdout;
pub async fn cmd(
cli: &UnitCtl,
filename: &String
) -> Result<(), UnitctlError> {
if !filename.ends_with(".tar") {
eprintln!("Warning: writing uncompressed tarball to {}", filename);
}
let control_socket = wait::wait_for_socket(cli).await?;
let client = UnitClient::new(control_socket);
let config_res = serde_json::to_string_pretty(
&send_empty_body_deserialize_response(&client, "GET", "/config").await?
);
if let Err(e) = config_res {
return Err(UnitctlError::DeserializationError{message: e.to_string()})
}
let current_config = config_res
.unwrap()
.into_bytes();
//let current_js_modules = send_empty_body_deserialize_response(&client, "GET", "/js_modules")
// .await?;
let mut conf_header = Header::new_gnu();
conf_header.set_size(current_config.len() as u64);
conf_header.set_mode(0o644);
conf_header.set_cksum();
// builder has a different type depending on output
if filename == "-" {
let mut ar = Builder::new(stdout());
ar.append_data(&mut conf_header, "config.json", current_config.as_slice()).unwrap();
} else {
let file = File::create(filename).unwrap();
let mut ar = Builder::new(file);
ar.append_data(&mut conf_header, "config.json", current_config.as_slice()).unwrap();
}
Ok(())
}

View file

@ -8,7 +8,11 @@ extern crate unit_client_rs;
use clap::Parser;
use crate::cmd::{applications, edit, execute as execute_cmd, import, instances, listeners, status};
use crate::cmd::{
applications, edit, execute as execute_cmd,
import, instances, listeners, status,
save
};
use crate::output_format::OutputFormat;
use crate::unitctl::{Commands, UnitCtl};
use crate::unitctl_error::UnitctlError;
@ -46,6 +50,8 @@ async fn main() -> Result<(), UnitctlError> {
Commands::Status { output_format } => status::cmd(&cli, output_format).await,
Commands::Listeners { output_format } => listeners::cmd(&cli, output_format).await,
Commands::Export { ref filename } => save::cmd(&cli, filename).await,
}
.map_err(|error| {
eprint_error(&error);

View file

@ -116,6 +116,16 @@ pub(crate) enum Commands {
},
#[command(about = "List all configured Unit applications")]
App(ApplicationArgs),
#[command(about = "Export the current configuration of UNIT")]
Export {
#[arg(
required = true,
short = 'f',
help = "tarball filename to save configuration to"
)]
filename: String
},
}
#[derive(Debug, Args)]