]> git.proxmox.com Git - proxmox-backup.git/commitdiff
src/api2/admin/datastore/backup.rs: implement config file upload
authorDietmar Maurer <dietmar@proxmox.com>
Mon, 3 Jun 2019 05:46:49 +0000 (07:46 +0200)
committerDietmar Maurer <dietmar@proxmox.com>
Mon, 3 Jun 2019 07:45:14 +0000 (09:45 +0200)
src/api2/admin/datastore/backup.rs
src/api2/admin/datastore/backup/upload_chunk.rs
src/client/http_client.rs

index 2495fd0602bb9e7ab311968fb633d5d76e7e8560..4e5e701ae3294ca245314064a3bb77722a1d0eb6 100644 (file)
@@ -150,6 +150,10 @@ fn upgrade_to_backup_protocol(
 fn backup_api() -> Router {
 
     let router = Router::new()
+        .subdir(
+            "config", Router::new()
+                .upload(api_method_upload_config())
+        )
         .subdir(
             "dynamic_chunk", Router::new()
                 .upload(api_method_upload_dynamic_chunk())
index ae0d3db220c88b63be2fd221da4382cd11963857..1cc674b379affa00a64d7952901835afe7e015d7 100644 (file)
@@ -185,3 +185,65 @@ fn upload_speedtest(
 
     Ok(Box::new(resp))
 }
+
+pub fn api_method_upload_config() -> ApiAsyncMethod {
+    ApiAsyncMethod::new(
+        upload_config,
+        ObjectSchema::new("Upload configuration file.")
+            .required("file-name", crate::api2::types::BACKUP_ARCHIVE_NAME_SCHEMA.clone())
+            .required("size", IntegerSchema::new("File size.")
+                      .minimum(1)
+                      .maximum(1024*1024*16)
+            )
+    )
+}
+
+fn upload_config(
+    _parts: Parts,
+    req_body: Body,
+    param: Value,
+    _info: &ApiAsyncMethod,
+    rpcenv: Box<RpcEnvironment>,
+) -> Result<BoxFut, Error> {
+
+    let mut file_name = tools::required_string_param(&param, "file-name")?.to_owned();
+    let size = tools::required_integer_param(&param, "size")? as usize;
+
+    if !file_name.ends_with(".conf") {
+        bail!("wrong config file extension: '{}'", file_name);
+    } else {
+        file_name.push_str(".zstd");
+    }
+
+    let env: &BackupEnvironment = rpcenv.as_ref();
+
+    let mut path = env.datastore.base_path();
+    path.push(env.backup_dir.relative_path());
+    path.push(&file_name);
+
+    let env2 = env.clone();
+    let env3 = env.clone();
+
+    let resp = req_body
+        .map_err(Error::from)
+        .concat2()
+        .and_then(move |data| {
+            if size != data.len() {
+                bail!("got configuration file with unexpected length ({} != {})", size, data.len());
+            }
+
+            let data = zstd::block::compress(&data, 0)?;
+
+            tools::file_set_contents(&path, &data, None)?;
+
+            env2.debug(format!("upload config {:?} ({} bytes, comp: {})", path, size, data.len()));
+
+            Ok(())
+        })
+        .and_then(move |_| {
+            Ok(env3.format_response(Ok(Value::Null)))
+        })
+        ;
+
+    Ok(Box::new(resp))
+}
index 8927ffb86620d4c35ccbac92e3f915eb39c221fd..1f33bc0369aa968b9289c1e16a8c5210bc1e7ffe 100644 (file)
@@ -431,6 +431,31 @@ impl BackupClient {
         self.h2.clone().post("finish", None).map(|_| ())
     }
 
+    pub fn upload_config(
+        &self,
+        file_name: &str,
+        src_path: std::path::PathBuf,
+    ) -> impl Future<Item=(), Error=Error> {
+
+        let h2 = self.h2.clone();
+        let file_name = file_name.to_owned();
+
+        let task = tokio::fs::File::open(src_path)
+            .map_err(Error::from)
+            .and_then(|file| {
+                let contents = vec![];
+                tokio::io::read_to_end(file, contents)
+                    .map_err(Error::from)
+                    .and_then(move |(_, contents)| {
+                        let param = json!({"size": contents.len(), "file-name": file_name });
+                        h2.upload("config", Some(param), contents)
+                            .map(|_| {})
+                    })
+            });
+
+        task
+    }
+
     pub fn upload_stream(
         &self,
         archive_name: &str,