369 lines
12 KiB
Rust
369 lines
12 KiB
Rust
#![feature(proc_macro_hygiene, decl_macro)]
|
|
extern crate rocket;
|
|
extern crate serde;
|
|
#[macro_use]
|
|
extern crate serde_derive;
|
|
extern crate nixideserver_lib;
|
|
extern crate serde_json;
|
|
|
|
use nixideserver_lib::*;
|
|
use rocket::http::Status;
|
|
use std::fs;
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::Command;
|
|
use std::thread;
|
|
|
|
// podman create --name test -p 3000:3000 -v $PWD:/nixide -w /nixide docker.io/nixos/nix nix-shell --pure start-ide.nix --run run_ide.sh
|
|
|
|
#[derive(Serialize, Deserialize, Clone)]
|
|
pub enum PodmanState {
|
|
Unkown,
|
|
FetchingSource,
|
|
StartingContainer,
|
|
RunningContainer,
|
|
StoppingContainer,
|
|
StoppedContainer,
|
|
DeletingContainer,
|
|
DeletingSources,
|
|
}
|
|
#[derive(Serialize, Deserialize, Clone)]
|
|
pub struct PodmanContainer {
|
|
ide_id: String,
|
|
state: PodmanState,
|
|
ide_state: IdeState,
|
|
ide_param: NixIdeServerParam,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone)]
|
|
pub struct PodmanContainerList {
|
|
container: Vec<PodmanContainer>,
|
|
}
|
|
|
|
struct ThreadRunningParam {
|
|
ide_folder: String,
|
|
container: PodmanContainer,
|
|
param: OpenGitParam,
|
|
working_folder: PathBuf,
|
|
}
|
|
|
|
pub struct PodmanEngine {
|
|
working_folder: PathBuf,
|
|
list: PodmanContainerList,
|
|
}
|
|
|
|
impl PodmanEngine {
|
|
pub fn new(working_folder: PathBuf) -> PodmanEngineTraitImpl {
|
|
PodmanEngineTraitImpl { working_folder }
|
|
}
|
|
|
|
fn _new(working_folder: PathBuf) -> Self {
|
|
let list_path = format!("{}/.podman_containers", &working_folder.display());
|
|
let list = match read_from_list_file(&list_path) {
|
|
Ok(p) => p,
|
|
_ => PodmanContainerList {
|
|
container: Vec::new(),
|
|
},
|
|
};
|
|
|
|
Self {
|
|
working_folder,
|
|
list,
|
|
}
|
|
}
|
|
|
|
pub fn save(&self) -> Result<(), std::io::Error> {
|
|
let json_string = serde_json::to_string_pretty(&self.list).unwrap(); // .map_err(|_| std::io::Error::from(std::io::ErrorKind::InvalidData))?;
|
|
fs::write(
|
|
&format!("{}/.podman_containers", &self.working_folder.display()),
|
|
json_string,
|
|
)
|
|
}
|
|
|
|
pub fn reload(trait_impl: &PodmanEngineTraitImpl) -> Result<Self, std::io::Error> {
|
|
Ok(Self::_new(trait_impl.working_folder.clone()))
|
|
}
|
|
|
|
fn find_mut_first_container(&mut self, ide_id: &str) -> Option<&mut PodmanContainer> {
|
|
self.list.container.iter_mut().find(|i| i.ide_id.eq(ide_id))
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
struct PodmanIdeStatus {
|
|
ide_state: IdeState,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct PodmanEngineTraitImpl {
|
|
working_folder: PathBuf,
|
|
}
|
|
|
|
impl NixIdeManageServiceEngine for PodmanEngineTraitImpl {
|
|
fn fetch_current_ide_state(&self, ide_id: &str) -> Result<IdeState, Status> {
|
|
let mut eng = PodmanEngine::reload(self)
|
|
.map_err(|_| Status::new(500, "internal error : reload engine"))?;
|
|
match eng.find_mut_first_container(ide_id) {
|
|
Some(i) => Ok(i.ide_state.clone()),
|
|
_ => Err(Status::NotFound),
|
|
}
|
|
}
|
|
|
|
fn start_open(&self, ide_id: &str, param: &OpenGitParam) -> Result<IdeState, Status> {
|
|
let mut eng = PodmanEngine::reload(self)
|
|
.map_err(|_| Status::new(500, "internal error : reload engine"))?;
|
|
match eng.find_mut_first_container(ide_id) {
|
|
Some(i) => Ok(i.ide_state.clone()),
|
|
_ => {
|
|
let ide_folder = create_ide_folder_path(&self.working_folder, ide_id);
|
|
fs::create_dir_all(&ide_folder)
|
|
.map_err(|_| Status::new(500, "internal error : can't create ide folder"))?;
|
|
let listen_port = eng
|
|
.list
|
|
.container
|
|
.iter()
|
|
.max_by_key(|i| i.ide_param.listen_port)
|
|
.and_then(|i| Some(i.ide_param.listen_port + 1))
|
|
.or(Some(3000))
|
|
.unwrap();
|
|
let ide_param = NixIdeServerParam {
|
|
listen_address: default_listen_address(),
|
|
listen_port,
|
|
app_folder: default_app_folder(),
|
|
project_folder: default_project_folder(),
|
|
working_folder: default_working_folder(),
|
|
};
|
|
|
|
let container = PodmanContainer {
|
|
ide_id: ide_id.to_owned(),
|
|
ide_state: IdeState::OPENING,
|
|
state: PodmanState::FetchingSource,
|
|
ide_param,
|
|
};
|
|
|
|
eng.list.container.push(container.clone());
|
|
eng.save()
|
|
.map_err(|_| Status::new(500, "internal error: can't save curent state"))?;
|
|
|
|
let thread_param = ThreadRunningParam {
|
|
ide_folder,
|
|
container,
|
|
param: param.clone(),
|
|
working_folder: eng.working_folder,
|
|
};
|
|
|
|
thread::spawn(move || {
|
|
match working_thread(&thread_param) {
|
|
Err(_) => clean_thread(&thread_param),
|
|
_ => {}
|
|
};
|
|
});
|
|
|
|
Ok(IdeState::OPENING)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone)]
|
|
pub struct NixIdeServerParam {
|
|
#[serde(default = "default_listen_address")]
|
|
listen_address: String,
|
|
#[serde(default = "default_listen_port")]
|
|
listen_port: u32,
|
|
#[serde(default = "default_app_folder")]
|
|
app_folder: String,
|
|
#[serde(default = "default_project_folder")]
|
|
project_folder: String,
|
|
#[serde(default = "default_working_folder")]
|
|
working_folder: String,
|
|
}
|
|
|
|
impl Default for NixIdeServerParam {
|
|
fn default() -> Self {
|
|
Self {
|
|
listen_address: default_listen_address(),
|
|
listen_port: default_listen_port(),
|
|
app_folder: default_app_folder(),
|
|
project_folder: default_project_folder(),
|
|
working_folder: default_working_folder(),
|
|
}
|
|
}
|
|
}
|
|
fn default_listen_address() -> String {
|
|
"0.0.0.0".to_owned()
|
|
}
|
|
|
|
fn default_listen_port() -> u32 {
|
|
3000
|
|
}
|
|
|
|
fn default_app_folder() -> String {
|
|
"_theiaideApp".to_owned()
|
|
}
|
|
|
|
fn default_project_folder() -> String {
|
|
"$PWD".to_owned()
|
|
}
|
|
|
|
fn default_working_folder() -> String {
|
|
"$PWD".to_owned()
|
|
}
|
|
|
|
fn create_ide_folder_path(working_folder_path: &Path, ide_id: &str) -> String {
|
|
format!("{}/{}", working_folder_path.display(), ide_id)
|
|
}
|
|
|
|
fn read_from_list_file(path: &str) -> Result<PodmanContainerList, std::io::Error> {
|
|
match fs::read_to_string(path) {
|
|
Ok(json_string) => serde_json::from_str::<PodmanContainerList>(&json_string)
|
|
.map_err(|_| std::io::Error::from(std::io::ErrorKind::InvalidData)),
|
|
Err(e) => Err(e),
|
|
}
|
|
}
|
|
|
|
fn fetch_sources(repo_path: &str, param: &OpenGitParam) -> Result<(), std::io::Error> {
|
|
let cmd_result = match Command::new("git")
|
|
.arg("clone")
|
|
.arg("-n")
|
|
.arg(¶m.clone_url)
|
|
.arg(repo_path)
|
|
.status()
|
|
{
|
|
Ok(s) if s.success() => Command::new("git")
|
|
.current_dir(repo_path)
|
|
.arg("checkout")
|
|
.arg(¶m.ref_name)
|
|
.status(),
|
|
e => e,
|
|
};
|
|
|
|
match cmd_result {
|
|
Ok(s) if s.success() => Ok(()),
|
|
Err(e) => Err(e),
|
|
_ => Err(std::io::Error::from(std::io::ErrorKind::InvalidData)),
|
|
}
|
|
}
|
|
|
|
fn clean_thread(thread_param: &ThreadRunningParam) {
|
|
let mut eng = PodmanEngine::_new(thread_param.working_folder.clone());
|
|
eng.list
|
|
.container
|
|
.iter()
|
|
.position(|i| i.ide_id.eq(&thread_param.container.ide_id))
|
|
.and_then(|ix| {
|
|
eng.list.container.remove(ix);
|
|
Some(0)
|
|
})
|
|
.unwrap_or_default();
|
|
eng.save().unwrap_or_default();
|
|
// todo delete container
|
|
fs::remove_dir_all(eng.working_folder).unwrap_or_default();
|
|
}
|
|
|
|
fn working_thread(thread_param: &ThreadRunningParam) -> Result<(), Error> {
|
|
let ide_folder = &thread_param.ide_folder;
|
|
let param = &thread_param.param;
|
|
fetch_sources(&format!("{}/repo", ide_folder), ¶m).map_err(|_| Error {})?;
|
|
let mut eng = PodmanEngine::_new(thread_param.working_folder.clone());
|
|
let ide_id = &thread_param.container.ide_id;
|
|
match eng.find_mut_first_container(&ide_id) {
|
|
Some(i) => {
|
|
i.state = PodmanState::StartingContainer;
|
|
eng.save().map_err(|_| Error {})?;
|
|
}
|
|
_ => {}
|
|
};
|
|
|
|
let port = thread_param.container.ide_param.listen_port;
|
|
let nix_expression = format!("with import <nixpkgs> {{}}; callPackage /nixide/repo/.nixide/start-ide.nix {{listenPort = {}; projectFolder = \"/nixide/repo\"; }}", port);
|
|
let out = Command::new("podman")
|
|
.current_dir(ide_folder)
|
|
.arg("create")
|
|
.arg("--name")
|
|
.arg(ide_id)
|
|
.arg("-p")
|
|
.arg(format!("{}:{}", port, port))
|
|
.arg("-v")
|
|
.arg(format!("{}:/nixide", ide_folder))
|
|
.arg("-w")
|
|
.arg("/nixide")
|
|
.arg("docker.io/nixos/nix")
|
|
.arg("nix-shell")
|
|
.arg("--expr")
|
|
.arg(nix_expression)
|
|
.arg("--pure")
|
|
.arg("--run")
|
|
.arg("run_ide.sh")
|
|
.status().map_err(|_| Error{})?;
|
|
|
|
// let nix_expression = format!("with import <nixpkgs> {{}}; callPackage {} {}", file, args);
|
|
// let cmd = Command::new("nix-shell")
|
|
// .stderr(Stdio::null())
|
|
// //.stdout(Stdio::null())
|
|
// .stdout(Stdio::piped())
|
|
// .arg("--expr")
|
|
// .arg(nix_expression)
|
|
// .arg("--pure")
|
|
// .arg("--run")
|
|
// .arg(run_script)
|
|
// .spawn()
|
|
// .expect("start ide command failed");
|
|
// podman create --name test -p 3000:3000 -v $PWD:/nixide -w /nixide docker.io/nixos/nix nix-shell --pure start-ide.nix --run run_ide.sh
|
|
// .arg("create")
|
|
// .arg("--name")
|
|
// .arg(format!("{:x}", hash))
|
|
// .arg("-p")
|
|
// .arg("3000:3000")
|
|
// .arg("docker.io/nixos/nix")
|
|
// // .arg(format!("git clone {}", param.clone_url))
|
|
// .output()
|
|
//
|
|
// String::from_utf8(out.stderr).unwrap()
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_fetch_sources() {
|
|
let now = std::time::SystemTime::now()
|
|
.duration_since(std::time::SystemTime::UNIX_EPOCH)
|
|
.unwrap();
|
|
let temp_dir = std::env::temp_dir();
|
|
let repo_path = format!("{}/{}", temp_dir.display(), now.as_nanos());
|
|
let git_param = OpenGitParam {
|
|
clone_url: "https://github.com/jmesmon/rust-hello-world.git".to_owned(),
|
|
inquirer: "stubbfel".to_owned(),
|
|
ref_name: "master".to_owned(),
|
|
};
|
|
assert_eq!(fetch_sources(&repo_path, &git_param).unwrap(), ());
|
|
}
|
|
|
|
#[test]
|
|
fn test_fetch_sources_wrong_ref() {
|
|
let now = std::time::SystemTime::now()
|
|
.duration_since(std::time::SystemTime::UNIX_EPOCH)
|
|
.unwrap();
|
|
let temp_dir = std::env::temp_dir();
|
|
let repo_path = format!("{}/{}", temp_dir.display(), now.as_nanos());
|
|
let git_param = OpenGitParam {
|
|
clone_url: "https://github.com/jmesmon/rust-hello-world.git".to_owned(),
|
|
inquirer: "stubbfel".to_owned(),
|
|
ref_name: "master2".to_owned(),
|
|
};
|
|
assert_eq!(fetch_sources(&repo_path, &git_param).is_err(), true);
|
|
}
|
|
|
|
#[test]
|
|
fn test_fetch_sources_wrong_url() {
|
|
let now = std::time::SystemTime::now()
|
|
.duration_since(std::time::SystemTime::UNIX_EPOCH)
|
|
.unwrap();
|
|
let temp_dir = std::env::temp_dir();
|
|
let repo_path = format!("{}/{}", temp_dir.display(), now.as_nanos());
|
|
let git_param = OpenGitParam {
|
|
clone_url: "https://bar.com/foo.git".to_owned(),
|
|
inquirer: "stubbfel".to_owned(),
|
|
ref_name: "master".to_owned(),
|
|
};
|
|
assert_eq!(fetch_sources(&repo_path, &git_param).is_err(), true);
|
|
}
|