add diskUsage effect (#2297)

This commit is contained in:
Aiden McClelland
2023-06-09 21:46:44 +00:00
committed by Matt Hill
parent 26ddf769b1
commit 71a15cf222
6 changed files with 112 additions and 2 deletions

1
backend/Cargo.lock generated
View File

@@ -2439,6 +2439,7 @@ dependencies = [
"dprint-swc-ext", "dprint-swc-ext",
"embassy_container_init", "embassy_container_init",
"helpers", "helpers",
"itertools 0.10.5",
"models", "models",
"reqwest", "reqwest",
"serde", "serde",

View File

@@ -28,6 +28,7 @@ magic-wormhole
ncdu ncdu
net-tools net-tools
network-manager network-manager
nvme-cli
nyx nyx
openssh-server openssh-server
pgloader pgloader
@@ -35,6 +36,7 @@ postgresql
psmisc psmisc
rsync rsync
samba-common-bin samba-common-bin
smartmontools
sqlite3 sqlite3
squashfs-tools squashfs-tools
systemd systemd

1
libs/Cargo.lock generated
View File

@@ -1647,6 +1647,7 @@ dependencies = [
"dprint-swc-ext", "dprint-swc-ext",
"embassy_container_init", "embassy_container_init",
"helpers", "helpers",
"itertools 0.10.5",
"models", "models",
"reqwest", "reqwest",
"serde", "serde",

View File

@@ -36,6 +36,7 @@ swc_macros_common = "=0.3.5"
swc_visit = "=0.3.0" swc_visit = "=0.3.0"
swc_visit_macros = "=0.3.1" swc_visit_macros = "=0.3.1"
sha2 = "0.10.2" sha2 = "0.10.2"
itertools = "0.10.5"
models = { path = "../models" } models = { path = "../models" }
helpers = { path = "../helpers" } helpers = { path = "../helpers" }
serde = { version = "1.0", features = ["derive", "rc"] } serde = { version = "1.0", features = ["derive", "rc"] }

View File

@@ -185,6 +185,14 @@ const runRsync = (
} }
}; };
const diskUsage = async ({
volumeId = requireParam("volumeId"),
path = requireParam("path"),
} = { volumeId: null, path: null }) => {
const [used, total] = await Deno.core.opAsync("disk_usage", volumeId, path);
return { used, total }
}
const currentFunction = Deno.core.opSync("current_function"); const currentFunction = Deno.core.opSync("current_function");
const input = Deno.core.opSync("get_input"); const input = Deno.core.opSync("get_input");
const variable_args = Deno.core.opSync("get_variable_args"); const variable_args = Deno.core.opSync("get_variable_args");
@@ -213,7 +221,8 @@ const effects = {
runDaemon, runDaemon,
signalGroup, signalGroup,
runRsync, runRsync,
readDir readDir,
diskUsage,
}; };
const defaults = { const defaults = {

View File

@@ -284,6 +284,7 @@ impl JsExecutionEnvironment {
fns::create_dir::decl(), fns::create_dir::decl(),
fns::remove_dir::decl(), fns::remove_dir::decl(),
fns::read_dir::decl(), fns::read_dir::decl(),
fns::disk_usage::decl(),
fns::current_function::decl(), fns::current_function::decl(),
fns::log_trace::decl(), fns::log_trace::decl(),
fns::log_warn::decl(), fns::log_warn::decl(),
@@ -396,10 +397,12 @@ mod fns {
SendSignal, SendSignalParams, SignalGroup, SignalGroupParams, SendSignal, SendSignalParams, SignalGroup, SignalGroupParams,
}; };
use helpers::{to_tmp_path, AtomicFile, Rsync, RsyncOptions}; use helpers::{to_tmp_path, AtomicFile, Rsync, RsyncOptions};
use models::VolumeId; use itertools::Itertools;
use models::{ErrorKind, VolumeId};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::{json, Value}; use serde_json::{json, Value};
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
use tokio::process::Command;
use super::{AnswerState, JsContext}; use super::{AnswerState, JsContext};
use crate::{system_time_as_unix_ms, MetadataJs, ResultType}; use crate::{system_time_as_unix_ms, MetadataJs, ResultType};
@@ -490,6 +493,7 @@ mod fns {
.ok_or_else(|| anyhow!("There is no {} in volumes", volume_id))? .ok_or_else(|| anyhow!("There is no {} in volumes", volume_id))?
}; };
//get_path_for in volume.rs //get_path_for in volume.rs
let path_in = path_in.strip_prefix("/").unwrap_or(&path_in);
let new_file = volume_path.join(path_in); let new_file = volume_path.join(path_in);
if !is_subset(&volume_path, &new_file).await? { if !is_subset(&volume_path, &new_file).await? {
bail!( bail!(
@@ -515,6 +519,7 @@ mod fns {
.ok_or_else(|| anyhow!("There is no {} in volumes", volume_id))? .ok_or_else(|| anyhow!("There is no {} in volumes", volume_id))?
}; };
//get_path_for in volume.rs //get_path_for in volume.rs
let path_in = path_in.strip_prefix("/").unwrap_or(&path_in);
let new_file = volume_path.join(path_in); let new_file = volume_path.join(path_in);
if !is_subset(&volume_path, &new_file).await? { if !is_subset(&volume_path, &new_file).await? {
bail!( bail!(
@@ -573,6 +578,7 @@ mod fns {
bail!("Volume {} is readonly", volume_id); bail!("Volume {} is readonly", volume_id);
} }
let path_in = path_in.strip_prefix("/").unwrap_or(&path_in);
let new_file = volume_path.join(&path_in); let new_file = volume_path.join(&path_in);
let parent_new_file = new_file let parent_new_file = new_file
.parent() .parent()
@@ -629,6 +635,7 @@ mod fns {
bail!("Volume {} is readonly", dst_volume); bail!("Volume {} is readonly", dst_volume);
} }
let src_path = src_path.strip_prefix("/").unwrap_or(&src_path);
let old_file = volume_path.join(src_path); let old_file = volume_path.join(src_path);
let parent_old_file = old_file let parent_old_file = old_file
.parent() .parent()
@@ -642,6 +649,7 @@ mod fns {
); );
} }
let dst_path = dst_path.strip_prefix("/").unwrap_or(&dst_path);
let new_file = volume_path_out.join(dst_path); let new_file = volume_path_out.join(dst_path);
let parent_new_file = new_file let parent_new_file = new_file
.parent() .parent()
@@ -689,6 +697,7 @@ mod fns {
bail!("Volume {} is readonly", dst_volume); bail!("Volume {} is readonly", dst_volume);
} }
let src_path = src_path.strip_prefix("/").unwrap_or(&src_path);
let src = volume_path.join(src_path); let src = volume_path.join(src_path);
// With the volume check // With the volume check
if !is_subset(&volume_path, &src).await? { if !is_subset(&volume_path, &src).await? {
@@ -702,6 +711,7 @@ mod fns {
bail!("Source at {} does not exists", src.to_string_lossy()); bail!("Source at {} does not exists", src.to_string_lossy());
} }
let dst_path = src_path.strip_prefix("/").unwrap_or(&dst_path);
let dst = volume_path_out.join(dst_path); let dst = volume_path_out.join(dst_path);
// With the volume check // With the volume check
if !is_subset(&volume_path_out, &dst).await? { if !is_subset(&volume_path_out, &dst).await? {
@@ -776,6 +786,7 @@ mod fns {
if volumes.readonly(&volume_id) { if volumes.readonly(&volume_id) {
bail!("Volume {} is readonly", volume_id); bail!("Volume {} is readonly", volume_id);
} }
let path_in = path_in.strip_prefix("/").unwrap_or(&path_in);
let new_file = volume_path.join(path_in); let new_file = volume_path.join(path_in);
// With the volume check // With the volume check
if !is_subset(&volume_path, &new_file).await? { if !is_subset(&volume_path, &new_file).await? {
@@ -806,6 +817,7 @@ mod fns {
if volumes.readonly(&volume_id) { if volumes.readonly(&volume_id) {
bail!("Volume {} is readonly", volume_id); bail!("Volume {} is readonly", volume_id);
} }
let path_in = path_in.strip_prefix("/").unwrap_or(&path_in);
let new_file = volume_path.join(path_in); let new_file = volume_path.join(path_in);
// With the volume check // With the volume check
if !is_subset(&volume_path, &new_file).await? { if !is_subset(&volume_path, &new_file).await? {
@@ -836,6 +848,7 @@ mod fns {
if volumes.readonly(&volume_id) { if volumes.readonly(&volume_id) {
bail!("Volume {} is readonly", volume_id); bail!("Volume {} is readonly", volume_id);
} }
let path_in = path_in.strip_prefix("/").unwrap_or(&path_in);
let new_file = volume_path.join(path_in); let new_file = volume_path.join(path_in);
// With the volume check // With the volume check
@@ -864,6 +877,7 @@ mod fns {
.ok_or_else(|| anyhow!("There is no {} in volumes", volume_id))?; .ok_or_else(|| anyhow!("There is no {} in volumes", volume_id))?;
volume_path volume_path
}; };
let path_in = path_in.strip_prefix("/").unwrap_or(&path_in);
let new_file = volume_path.join(path_in); let new_file = volume_path.join(path_in);
// With the volume check // With the volume check
@@ -893,6 +907,86 @@ mod fns {
Ok(paths) Ok(paths)
} }
#[op]
async fn disk_usage(
state: Rc<RefCell<OpState>>,
volume_id: Option<VolumeId>,
path_in: Option<PathBuf>,
) -> Result<(u64, u64), AnyError> {
let (base_path, volume_path) = {
let state = state.borrow();
let ctx: &JsContext = state.borrow();
let volume_path = if let Some(volume_id) = volume_id {
Some(
ctx.volumes
.path_for(&ctx.datadir, &ctx.package_id, &ctx.version, &volume_id)
.ok_or_else(|| anyhow!("There is no {} in volumes", volume_id))?,
)
} else {
None
};
(ctx.datadir.join("package-data"), volume_path)
};
let path = if let (Some(volume_path), Some(path_in)) = (volume_path, path_in) {
let path_in = path_in.strip_prefix("/").unwrap_or(&path_in);
Some(volume_path.join(path_in))
} else {
None
};
if let Some(path) = path {
let size = String::from_utf8(
Command::new("df")
.arg("--output=size")
.arg("--block-size=1")
.arg(&base_path)
.stdout(std::process::Stdio::piped())
.output()
.await?
.stdout,
)?
.lines()
.skip(1)
.next()
.unwrap_or_default()
.parse()?;
let used = String::from_utf8(
Command::new("du")
.arg("-s")
.arg("--block-size=1")
.arg(path)
.stdout(std::process::Stdio::piped())
.output()
.await?
.stdout,
)?
.split_ascii_whitespace()
.next()
.unwrap_or_default()
.parse()?;
Ok((used, size))
} else {
String::from_utf8(
Command::new("df")
.arg("--output=used,size")
.arg("--block-size=1")
.arg(&base_path)
.stdout(std::process::Stdio::piped())
.output()
.await?
.stdout,
)?
.lines()
.skip(1)
.next()
.unwrap_or_default()
.split_ascii_whitespace()
.next_tuple()
.and_then(|(used, size)| Some((used.parse().ok()?, size.parse().ok()?)))
.ok_or_else(|| anyhow!("invalid output from df"))
}
}
#[op] #[op]
fn current_function(state: &mut OpState) -> Result<String, AnyError> { fn current_function(state: &mut OpState) -> Result<String, AnyError> {
let ctx = state.borrow::<JsContext>(); let ctx = state.borrow::<JsContext>();
@@ -1216,6 +1310,7 @@ mod fns {
if volumes.readonly(&volume_id) { if volumes.readonly(&volume_id) {
bail!("Volume {} is readonly", volume_id); bail!("Volume {} is readonly", volume_id);
} }
let path_in = path_in.strip_prefix("/").unwrap_or(&path_in);
let new_file = volume_path.join(path_in); let new_file = volume_path.join(path_in);
// With the volume check // With the volume check
if !is_subset(&volume_path, &new_file).await? { if !is_subset(&volume_path, &new_file).await? {
@@ -1265,6 +1360,7 @@ mod fns {
if volumes.readonly(&volume_id) { if volumes.readonly(&volume_id) {
bail!("Volume {} is readonly", volume_id); bail!("Volume {} is readonly", volume_id);
} }
let path_in = path_in.strip_prefix("/").unwrap_or(&path_in);
let new_file = volume_path.join(path_in); let new_file = volume_path.join(path_in);
// With the volume check // With the volume check
if !is_subset(&volume_path, &new_file).await? { if !is_subset(&volume_path, &new_file).await? {