Skip to content

Commit

Permalink
env: VSCode with plugins
Browse files Browse the repository at this point in the history
feat: `codchi tar`
feat: `codchi debug-store`
  • Loading branch information
htngr committed Dec 11, 2024
1 parent 8895e89 commit 5b15198
Show file tree
Hide file tree
Showing 19 changed files with 279 additions and 65 deletions.
7 changes: 0 additions & 7 deletions .vscode/extensions.json

This file was deleted.

13 changes: 6 additions & 7 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
{
"editor.formatOnSave": true,
"files.autoSave": "onFocusChange",
"rust-analyzer.linkedProjects": [
"./codchi/cli/Cargo.toml"
],
"rust-analyzer.showUnlinkedFileNotification": false,
}
"editor.formatOnSave": true,
"files.autoSave": "onFocusChange",
"rust-analyzer.showUnlinkedFileNotification": false,
"nix.enableLanguageServer": true,
"nix.serverPath": "nil"
}
4 changes: 2 additions & 2 deletions codchi/shell.nix
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

, codchi

, nixd
, nil
, nixpkgs-fmt
, strace
, gdb
Expand Down Expand Up @@ -50,7 +50,7 @@ mkShell (lib.recursiveUpdate
inputsFrom = [ codchi ];

packages = [
nixd
nil
nixpkgs-fmt

codchi.passthru.rust
Expand Down
16 changes: 16 additions & 0 deletions codchi/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use git_url_parse::{GitUrl, Scheme};
use lazy_regex::regex_is_match;
use log::Level;
use std::{
path::PathBuf,
str::FromStr,
sync::{LazyLock, OnceLock},
};
Expand Down Expand Up @@ -450,6 +451,21 @@ See the following docs on how to register the completions with your shell:
/// Start the codchi tray if not running.
#[clap(hide = true)]
Tray {},

#[clap(about = "Export the file system of a code machine.")]
Tar {
/// Name of the code machine
name: String,

/// Path to export to.
target_file: PathBuf,
},

#[clap(
about = "Open a debug shell inside `codchistore` without starting \
/ requiring any services. Usefull for debugging."
)]
DebugStore,
}

mod module {
Expand Down
2 changes: 1 addition & 1 deletion codchi/src/logging/output.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::{fmt::Display, io::stdout};

use crate::config::{MachineModules, MachineStatus, Mod, ModLsOutput, StatusOutput};
use itertools::Itertools;
use serde::Serialize;
use crate::config::{MachineModules, MachineStatus, Mod, ModLsOutput, StatusOutput};

use crate::platform::{ConfigStatus, Machine};

Expand Down
2 changes: 1 addition & 1 deletion codchi/src/logging/progress.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use super::nix::{self, Activity, ActivityType, LogItem, LogResult};
use crate::util::store_path_base;
use console::style;
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use itertools::Itertools;
use log::Level;
use number_prefix::NumberPrefix;
use crate::util::store_path_base;
use std::{borrow::Cow, collections::HashMap, sync::LazyLock, time::Duration};
use throttle::Throttle;

Expand Down
43 changes: 32 additions & 11 deletions codchi/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use crate::{
};
use clap::{CommandFactory, Parser};
use config::{git_url::GitUrl, CodchiConfig, MachineConfig};
use logging::CodchiOutput;
use platform::{ConfigStatus, Host};
use logging::{set_progress_status, CodchiOutput};
use platform::{store_debug_shell, ConfigStatus, Host, MachineDriver};
use std::{env, process::exit};
use util::{ResultExt, UtilExt};

Expand All @@ -27,6 +27,7 @@ fn main() -> anyhow::Result<()> {

let cli = Cli::parse();

// process immediate commands
if let Some(Cmd::Completion { shell }) = &cli.command {
shell.generate(&mut Cli::command(), &mut std::io::stdout());
exit(0);
Expand All @@ -39,11 +40,25 @@ fn main() -> anyhow::Result<()> {
// preload config
let cfg = CodchiConfig::get();

if !matches!(cli.command, Some(Cmd::Tray {})) && cfg.tray.autostart {
Driver::host()
.start_tray(false)
.trace_err("Failed starting codchi's tray")
.ignore();
// process commands without the store commands
match &cli.command {
Some(Cmd::Tray {}) if cfg.tray.autostart => {
Driver::host()
.start_tray(false)
.trace_err("Failed starting codchi's tray")
.ignore();
exit(0);
}
Some(Cmd::Tar { name, target_file }) => {
progress_scope! {
set_progress_status(format!("Exporting files of {name} to {target_file:?}..."));
Machine::by_name(name, false)?.tar(target_file)?;
log::info!("Success! Exported file system of machine {name} to {target_file:?}");
}
exit(0);
}
Some(Cmd::DebugStore) => store_debug_shell()?,
_ => {}
}

CLI_ARGS
Expand All @@ -52,8 +67,9 @@ fn main() -> anyhow::Result<()> {

let _ = Driver::store();

// all other commands
match &cli.command.unwrap_or(Cmd::Status {}) {
Cmd::Status {} => Machine::list()?.print(cli.json),
Cmd::Status {} => Machine::list(true)?.print(cli.json),
Cmd::Init {
machine_name,
url,
Expand Down Expand Up @@ -99,12 +115,15 @@ fn main() -> anyhow::Result<()> {
"Machine '{machine_name}' is ready! Use `codchi exec {machine_name}` to start it."
);
}
Cmd::Rebuild { no_update, name } => Machine::by_name(name)?.build(*no_update)?,
Cmd::Exec { name, cmd } => Machine::by_name(name)?.exec(cmd)?,
Cmd::Rebuild { no_update, name } => {
Machine::by_name(name, true)?.build(*no_update)?;
log::info!("Machine {name} rebuilt successfully!");
}
Cmd::Exec { name, cmd } => Machine::by_name(name, true)?.exec(cmd)?,
Cmd::Delete {
name,
i_am_really_sure,
} => Machine::by_name(name)?.delete(*i_am_really_sure)?,
} => Machine::by_name(name, true)?.delete(*i_am_really_sure)?,
Cmd::Module(cmd) => match cmd {
cli::ModuleCmd::List { name } => {
let json = cli.json;
Expand Down Expand Up @@ -157,6 +176,8 @@ fn main() -> anyhow::Result<()> {
} => Driver::store().gc(delete_old.map(|x| x.unwrap_or_default()), *all, machines)?,
Cmd::Tray {} => tray::run()?,
Cmd::Completion { .. } => unreachable!(),
Cmd::Tar { .. } => unreachable!(),
Cmd::DebugStore => unreachable!(),
}
if CodchiConfig::get().tray.autostart {
Driver::host()
Expand Down
6 changes: 3 additions & 3 deletions codchi/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ pub fn add(
}
cfg.write(lock)?;

let machine = Machine::by_name(machine_name)?;
let machine = Machine::by_name(machine_name, true)?;
machine.write_flake()?;
machine.update_status()
}
Expand Down Expand Up @@ -244,7 +244,7 @@ pub fn set(
}
cfg.write(lock)?;

let machine = Machine::by_name(machine_name)?;
let machine = Machine::by_name(machine_name, true)?;
machine.write_flake()?;
machine.update_status()
}
Expand All @@ -269,7 +269,7 @@ pub fn delete(machine_name: &str, module_name: &ModuleName) -> Result<Machine> {
}
cfg.write(lock)?;

let machine = Machine::by_name(machine_name)?;
let machine = Machine::by_name(machine_name, true)?;
machine.write_flake()?;
machine.update_status()
}
Expand Down
2 changes: 1 addition & 1 deletion codchi/src/platform/cmd/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::*;
use crate::util::UtilExt;
use anyhow::anyhow;
use serde::Deserialize;
use crate::util::UtilExt;
use std::io::{BufRead, BufReader, Read};
use std::process::{Child, Stdio};
use std::sync::mpsc::{channel, Receiver, RecvTimeoutError, Sender};
Expand Down
13 changes: 13 additions & 0 deletions codchi/src/platform/linux/lxd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@ pub mod container {
Ok(())
}

pub fn export(name: &str, target_path: &str) -> Result<()> {
let mut cmd = lxc_command(&[
"export",
"--instance-only",
"--compression",
"none",
name,
target_path,
]);
cmd.wait_ok()?;
Ok(())
}

pub fn get_info(name: &str) -> Result<Option<Info>> {
let info = lxc_command(&["list", "--format", "json"])
.output_json::<Vec<Info>>()?
Expand Down
110 changes: 108 additions & 2 deletions codchi/src/platform/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,23 @@ use super::{Driver, LinuxCommandTarget, LinuxUser, NixDriver, Store};
use crate::{
cli::DEBUG,
consts::{self, machine::machine_name, store, user, ToPath},
logging::{log_progress, set_progress_status},
logging::{log_progress, set_progress_status, with_suspended_progress},
platform::{
platform::lxd::container::LxdDevice, CommandExt, Machine, MachineDriver, PlatformStatus,
},
util::{with_tmp_file, LinuxPath, PathExt, ResultExt, UtilExt},
};
use anyhow::{Context, Result};
use anyhow::{bail, Context, Result};
pub use host::*;
use inquire::Confirm;
use log::*;
use std::{
collections::HashMap,
env,
fs::{self, File},
io::Write,
path::PathBuf,
process::Command,
sync::mpsc::channel,
thread,
};
Expand Down Expand Up @@ -126,6 +128,15 @@ Please see <https://codchi.dev/introduction/installation#linux> for setup instru
}
}

pub fn store_debug_shell() -> anyhow::Result<()> {
LinuxCommandDriver {
container_name: consts::CONTAINER_STORE_NAME.to_string(),
}
.run("bash", &[])
.exec()?;
Ok(())
}

impl MachineDriver for Machine {
fn cmd(&self) -> impl LinuxCommandTarget {
LinuxCommandDriver {
Expand Down Expand Up @@ -325,6 +336,101 @@ tail -f "{log_file}"

cmd.with_user(LinuxUser::Root)
}

fn tar(&self, target_file: &std::path::Path) -> Result<()> {
fn command_with_privileges(reason: &str, command: &[&str]) -> Result<Command> {
let sudo_path = which::which("sudo").ok();
let doas_path = which::which("doas").ok();

let (sudo_name, sudo) = if let Some(path) = sudo_path {
("sudo", path)
} else if let Some(path) = doas_path {
("doas", path)
} else {
bail!(
"Neither sudo nor doas was found on this system. \
This is needed in order to {reason}."
);
};
log::debug!("Found {sudo_name} at {sudo:?}");

let message = format!(
"Codchi needs to invoke `{}` as root in order to {reason}. Is it okay to use {sudo_name}?",
command.join(" ")
);
let user_confirmed = Confirm::new(&message).with_default(true).prompt()?;

if user_confirmed {
let mut cmd = Command::new(sudo);
cmd.args(command);
Ok(cmd)
} else {
bail!("Operation was canceled by the user");
}
}

with_tmp_file(&format!("codchi-backup-{}", self.config.name), |tmp_dir| {
fs::create_dir_all(tmp_dir)?;
let lxc_export = tmp_dir.join("lxc_export.tar").display().to_string();
let target_file = target_file.display().to_string();
lxd::container::export(
&consts::machine::machine_name(&self.config.name),
&lxc_export,
)?;
Command::new("tar")
.args(["-C", &tmp_dir.display().to_string(), "-xf", &lxc_export])
.wait_ok()?;
with_suspended_progress(|| {
command_with_privileges(
"export the code machine root file system",
&[
"tar",
"-C",
&tmp_dir
.join("backup/container/rootfs")
.display()
.to_string(),
"-cf",
&target_file,
".",
],
)?
.wait_ok()?;

// add home dir
command_with_privileges(
"export the code machine home directory",
&[
"tar",
"--append",
"-f",
&target_file,
"-C",
&consts::host::DIR_DATA
.join_machine(&self.config.name)
.display()
.to_string(),
"--transform",
"s|^./|./home/codchi/|",
"--owner=1000",
"--group=100",
"--numeric-owner",
".",
],
)?
.wait_ok()?;

command_with_privileges(
"cleanup temporary files",
&["rm", "-rf", &tmp_dir.display().to_string()],
)?
.wait_ok()?;
Ok(())
})
})?;

Ok(())
}
}

#[derive(Debug, Clone)]
Expand Down
Loading

0 comments on commit 5b15198

Please sign in to comment.