feat: agent base implementation
Some checks failed
Rust CI / test (push) Failing after 18s

This commit is contained in:
2025-08-15 13:30:14 +09:00
commit 986cb5f1ac
18 changed files with 340 additions and 0 deletions

44
.github/workflows/test.yaml vendored Normal file
View File

@@ -0,0 +1,44 @@
name: Rust CI
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Rust
uses: actions/setup-rust@v2
with:
rust-version: stable
- name: Cache Cargo registry
uses: actions/cache@v3
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-registry-
- name: Cache Cargo build
uses: actions/cache@v3
with:
path: target
key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-build-
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose

51
.gitignore vendored Executable file
View File

@@ -0,0 +1,51 @@
# Created by https://www.toptal.com/developers/gitignore/api/rust,rust-analyzer,react
# Edit at https://www.toptal.com/developers/gitignore?templates=rust,rust-analyzer,react
### react ###
.DS_*
*.log
logs
**/*.backup.*
**/*.back.*
.env
**/.env
.env.*
**/.env.*
node_modules
bower_components
*.sublime*
psd
thumb
sketch
### Rust ###
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
### rust-analyzer ###
# Can be generated by other build systems other than cargo (ex: bazelbuild/rust_rules)
rust-project.json
# End of https://www.toptal.com/developers/gitignore/api/rust,rust-analyzer,react
.venv
build
.VSCodeCounter

1
README.md Normal file
View File

@@ -0,0 +1 @@
# Democracy Linux

3
agent/Cargo.toml Normal file
View File

@@ -0,0 +1,3 @@
[workspace]
resolver = "3"
members = ["crates/vm"]

View File

@@ -0,0 +1,7 @@
[package]
name = "dlx-vm"
version = "0.1.0"
edition = "2024"
[dependencies]
thiserror = "2.0.14"

View File

@@ -0,0 +1,9 @@
use thiserror::Error;
use crate::qemu::error::QemuError;
#[derive(Debug, Clone, Error)]
pub enum VmError {
#[error("QEMU error")]
Qemu(#[from] QemuError),
}

View File

@@ -0,0 +1,4 @@
mod error;
mod model;
mod qemu;
mod service;

View File

@@ -0,0 +1,3 @@
pub mod disk;
pub mod net;
pub mod vm;

View File

@@ -0,0 +1,7 @@
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Disk {
pub file: String,
pub index: u32,
pub format: Option<String>,
pub media: String,
}

View File

@@ -0,0 +1,2 @@
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VmNet(pub String);

View File

@@ -0,0 +1,15 @@
use crate::model::{disk::Disk, net::VmNet};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct MemoryMB(pub u32);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MachineAccel(pub String);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CloudInitVM {
pub mem: MemoryMB,
pub nets: Vec<VmNet>,
pub disks: Vec<Disk>,
pub machine: MachineAccel,
}

View File

@@ -0,0 +1,4 @@
pub mod config;
pub mod error;
pub mod param;
pub mod runner;

View File

@@ -0,0 +1,7 @@
use crate::qemu::param::QemuParam;
#[derive(Debug, Clone)]
pub struct QemuConfig {
pub executable: String,
pub param: QemuParam,
}

View File

@@ -0,0 +1,7 @@
use thiserror::Error;
#[derive(Debug, Clone, Error)]
pub enum QemuError {
#[error("unknown qemu error")]
Unknown,
}

View File

@@ -0,0 +1,158 @@
use crate::model::{
disk::Disk,
net::VmNet,
vm::{CloudInitVM, MachineAccel, MemoryMB},
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct QemuParam(pub String);
pub trait IntoQemuParam {
fn into_qemu_param(self) -> QemuParam;
}
impl IntoQemuParam for MachineAccel {
fn into_qemu_param(self) -> QemuParam {
QemuParam(format!("-machine accel={}", self.0))
}
}
impl IntoQemuParam for MemoryMB {
fn into_qemu_param(self) -> QemuParam {
QemuParam(format!("-m {}", self.0))
}
}
impl IntoQemuParam for VmNet {
fn into_qemu_param(self) -> QemuParam {
QemuParam(format!("-net {}", self.0))
}
}
impl IntoQemuParam for Disk {
fn into_qemu_param(self) -> QemuParam {
let mut v: Vec<String> = vec![];
v.push(format!("file={}", self.file));
v.push(format!("index={}", self.index));
if let Some(format) = self.format {
v.push(format!("format={}", format));
}
v.push(format!("media={}", self.media));
let v = v.join(",");
QemuParam(format!("-drive {}", v))
}
}
impl IntoQemuParam for CloudInitVM {
fn into_qemu_param(self) -> QemuParam {
let mut params: Vec<QemuParam> = vec![];
params.push(self.mem.into_qemu_param());
let nets: Vec<QemuParam> = self
.nets
.into_iter()
.map(IntoQemuParam::into_qemu_param)
.collect();
params.extend_from_slice(&nets);
let disks: Vec<QemuParam> = self
.disks
.into_iter()
.map(IntoQemuParam::into_qemu_param)
.collect();
params.extend_from_slice(&disks);
params.push(self.machine.into_qemu_param());
let param = params
.into_iter()
.map(|e| e.0)
.collect::<Vec<String>>()
.join(" ");
QemuParam(param)
}
}
#[cfg(test)]
mod tests {
use crate::{
model::{
disk::Disk,
net::VmNet,
vm::{CloudInitVM, MachineAccel, MemoryMB},
},
qemu::param::{IntoQemuParam, QemuParam},
};
#[test]
fn test_into_qemu_param_disk_format() {
let disk = Disk {
file: "f".into(),
index: 1,
format: Some("123".into()),
media: "m".into(),
};
let param = disk.into_qemu_param();
assert_eq!(
param,
QemuParam("-drive file=f,index=1,format=123,media=m".into())
)
}
#[test]
fn test_into_qemu_param_disk_no_format() {
let disk = Disk {
file: "f".into(),
index: 1,
format: None,
media: "m".into(),
};
let param = disk.into_qemu_param();
assert_eq!(param, QemuParam("-drive file=f,index=1,media=m".into()))
}
#[test]
fn test_into_qemu_param_mem() {
let param = MemoryMB(1024).into_qemu_param();
assert_eq!(param, QemuParam("-m 1024".into()));
}
#[test]
fn test_into_qemu_param_machine() {
let param = MachineAccel("kvm:tcg".into()).into_qemu_param();
assert_eq!(param, QemuParam("-machine accel=kvm:tcg".into()));
}
#[test]
fn test_into_qemu_param_net() {
let param = VmNet("nic".into()).into_qemu_param();
assert_eq!(param, QemuParam("-net nic".into()));
}
#[test]
fn test_into_qemu_param_vm() {
let vm = CloudInitVM {
mem: MemoryMB(1024),
nets: vec![VmNet("1".into()), VmNet("2".into())],
disks: vec![Disk {
file: "f".into(),
index: 1,
format: None,
media: "m".into(),
}],
machine: MachineAccel("kvm:tcg".into()),
};
let param = vm.into_qemu_param();
assert_eq!(
param.0,
"-m 1024 -net 1 -net 2 -drive file=f,index=1,media=m -machine accel=kvm:tcg"
);
}
}

View File

@@ -0,0 +1,15 @@
use std::process::Command;
use crate::qemu::config::QemuConfig;
pub fn run_qemu(config: &QemuConfig) {
let args: Vec<String> = config
.param
.clone()
.0
.split(" ")
.map(|s| s.to_string())
.collect();
let handle = Command::new(&config.executable).args(&args).spawn();
}

View File

@@ -0,0 +1 @@
pub mod runner;

View File

@@ -0,0 +1,2 @@
pub trait RunnerService {
}