cubic 0.13.0

Cubic is a lightweight command line manager for virtual machines. It has a simple, daemon-less and rootless design. All Cubic virtual machines run isolated in the user context. Cubic is built on top of QEMU, KVM and cloud-init. Show all supported images: $ cubic image ls Create a new virtual machine instance: $ cubic add mymachine --image ubuntu:noble List all virtual machine instances: $ cubic ls Start an instance: $ cubic start <instance name> Stop an instance: $ cubic stop <instance name> Open a shell in the instance: $ cubic ssh <machine name> Copy a file from the host to the instance: $ cubic scp <path/to/host/file> <machine>:<path/to/guest/file> Copy a file from the instance to the hots: $ cubic scp <machine>:<path/to/guest/file> <path/to/host/file>
pub mod image_dao;
pub mod image_factory;
pub mod image_fetcher;
pub mod image_store;
pub mod image_store_mock;

use crate::arch::Arch;
use crate::error::Error;
pub use image_dao::*;
pub use image_factory::*;
pub use image_fetcher::*;
pub use image_store::*;
use serde::{Deserialize, Serialize};
use std::io::{Read, Write};

#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct Image {
    pub vendor: String,
    pub codename: String,
    pub version: String,
    pub arch: Arch,
    pub url: String,
    pub size: Option<u64>,
}

impl Image {
    pub fn to_file_name(&self) -> String {
        format!("{}_{}_{}", self.vendor, self.codename, self.arch)
    }
}

#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
pub struct ImageCache {
    images: Vec<Image>,
    timestamp: u64,
}

impl ImageCache {
    pub fn deserialize(reader: &mut dyn Read) -> Result<ImageCache, Error> {
        serde_yaml::from_reader(reader).map_err(Error::SerdeYaml)
    }

    pub fn serialize(&self, writer: &mut dyn Write) -> Result<(), Error> {
        serde_yaml::to_writer(writer, self).map_err(Error::SerdeYaml)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::io::BufReader;

    #[test]
    fn test_deserialize_invalid() {
        let reader = &mut BufReader::new("".as_bytes());
        let cache = ImageCache::deserialize(reader);
        assert!(cache.is_err());
    }

    #[test]
    fn test_deserialize_empty() {
        let reader = &mut BufReader::new("images: []\ntimestamp: 0".as_bytes());
        let cache = ImageCache::deserialize(reader);
        assert_eq!(cache.unwrap(), ImageCache::default());
    }

    #[test]
    fn test_serialize_empty() {
        let mut writer = Vec::new();

        ImageCache::default().serialize(&mut writer).unwrap();

        assert_eq!(
            String::from_utf8(writer).unwrap(),
            "images: []\ntimestamp: 0\n"
        );
    }

    #[test]
    fn test_deserialize_single() {
        let reader = &mut BufReader::new("images: []\ntimestamp: 0".as_bytes());
        let cache = ImageCache::deserialize(reader);
        assert_eq!(cache.unwrap(), ImageCache::default());
    }

    #[test]
    fn test_serialize_single() {
        let mut writer = Vec::new();

        ImageCache {
            images: vec![Image {
                vendor: "testvendor".to_string(),
                codename: "testcodename".to_string(),
                version: "testversion".to_string(),
                arch: Arch::AMD64,
                url: "testurl".to_string(),
                size: None,
            }],
            timestamp: 1000,
        }
        .serialize(&mut writer)
        .unwrap();

        assert_eq!(
            String::from_utf8(writer).unwrap(),
            r#"images:
- vendor: testvendor
  codename: testcodename
  version: testversion
  arch: AMD64
  url: testurl
  size: null
timestamp: 1000
"#
        );
    }
}