~muirrum/solarctl

f7aedd98bff7b208da8dd70917fcf57fc140f0b7 — Cara Salter 1 year, 11 months ago d6ed1d4 master
Format
3 files changed, 106 insertions(+), 189 deletions(-)

D flake.lock
D flake.nix
M src/main.rs
D flake.lock => flake.lock +0 -74
@@ 1,74 0,0 @@
{
  "nodes": {
    "flake-utils": {
      "locked": {
        "lastModified": 1653893745,
        "narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
        "owner": "numtide",
        "repo": "flake-utils",
        "rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
        "type": "github"
      },
      "original": {
        "owner": "numtide",
        "repo": "flake-utils",
        "type": "github"
      }
    },
    "naersk": {
      "inputs": {
        "nixpkgs": "nixpkgs"
      },
      "locked": {
        "lastModified": 1654608517,
        "narHash": "sha256-KIxHjDDJYhoiLanLjpeAk5AuZsfip8M62JhkuloEGb0=",
        "owner": "nix-community",
        "repo": "naersk",
        "rev": "14997a79cd78fe34ad6390f18a327ee0593e5eec",
        "type": "github"
      },
      "original": {
        "owner": "nix-community",
        "repo": "naersk",
        "type": "github"
      }
    },
    "nixpkgs": {
      "locked": {
        "lastModified": 1654665288,
        "narHash": "sha256-7blJpfoZEu7GKb84uh3io/5eSJNdaagXD9d15P9iQMs=",
        "owner": "NixOS",
        "repo": "nixpkgs",
        "rev": "43ecbe7840d155fa933ee8a500fb00dbbc651fc8",
        "type": "github"
      },
      "original": {
        "id": "nixpkgs",
        "type": "indirect"
      }
    },
    "nixpkgs_2": {
      "locked": {
        "lastModified": 1654665288,
        "narHash": "sha256-7blJpfoZEu7GKb84uh3io/5eSJNdaagXD9d15P9iQMs=",
        "owner": "NixOS",
        "repo": "nixpkgs",
        "rev": "43ecbe7840d155fa933ee8a500fb00dbbc651fc8",
        "type": "github"
      },
      "original": {
        "id": "nixpkgs",
        "type": "indirect"
      }
    },
    "root": {
      "inputs": {
        "flake-utils": "flake-utils",
        "naersk": "naersk",
        "nixpkgs": "nixpkgs_2"
      }
    }
  },
  "root": "root",
  "version": 7
}

D flake.nix => flake.nix +0 -40
@@ 1,40 0,0 @@
{
  inputs = {
    flake-utils.url = "github:numtide/flake-utils";
    naersk.url = "github:nix-community/naersk";
  };

  outputs = { self, nixpkgs, flake-utils, naersk }:
    flake-utils.lib.eachDefaultSystem (
      system: let
        pkgs = nixpkgs.legacyPackages."${system}";
        naersk-lib = naersk.lib."${system}";

        deps = with pkgs; [
          openssl
          pkg-config
          libvirt
        ];
      in
        rec {
          # `nix build`
          packages.solarctl = naersk-lib.buildPackage {
            pname = "solarctl";
            root = ./.;
            nativeBuildInputs = deps;
          };
          defaultPackage = packages.solarctl;

          # `nix run`
          apps.solarctl = flake-utils.lib.mkApp {
            drv = packages.solarctl;
          };
          defaultApp = apps.solarctl;

          # `nix develop`
          devShell = pkgs.mkShell {
            nativeBuildInputs = with pkgs; [ rustc cargo ] ++ deps;
          };
        }
    );
}

M src/main.rs => src/main.rs +106 -75
@@ 5,11 5,11 @@ use clap::Parser;
mod errors;
use errors::CliError;
use reqwest::header;
use tabular::{row, Table};
use reqwest::{blocking::Client, StatusCode};
use solarlib::planet::{Planet, Memory, CpuCount};
use serde::{Deserialize, Serialize};
use solarlib::planet::{CpuCount, Memory, Planet};
use solarlib::star::NewPlanet;
use serde::{Serialize, Deserialize};
use tabular::{row, Table};

/// Manage solard and homeworld instances
#[derive(Parser)]


@@ 19,12 19,12 @@ struct Args {
    server: String,
    /// The action to be taken
    #[clap(subcommand)]
    action: Action
    action: Action,
}

#[derive(Serialize, Deserialize)]
struct ServerConfig {
    pub token: String
    pub token: String,
}

#[derive(clap::Subcommand)]


@@ 37,7 37,7 @@ enum Action {
        uuid: String,

        #[clap(long, short)]
        force: bool
        force: bool,
    },
    Start {
        /// The UUID of the machine to start


@@ 51,13 51,13 @@ enum Action {
        uuid: String,

        #[clap(long, short)]
        force: bool
        force: bool,
    },
    View {
        uuid: String
        uuid: String,
    },

    Create { 
    Create {
        max_mem: u64,

        max_cpus: u64,


@@ 67,14 67,14 @@ enum Action {
        name: String,

        /// The Sha256 hash of the ship
        ship: String
        ship: String,
    },

    /// Goes through the first-time authentication process to create a token that expires after one
    /// year, storing it in the process.
    Login {
        key: String
    }
        key: String,
    },
}

fn main() {


@@ 82,70 82,79 @@ fn main() {
    let args = Args::parse();
    let xdg_dirs = xdg::BaseDirectories::with_profile("solarctl", args.server.clone()).unwrap();

            let mut headers = header::HeaderMap::new();
    let mut headers = header::HeaderMap::new();

    if let Some(p) = xdg_dirs.find_config_file("config.json") {
        let c = fs::read_to_string(p).unwrap(); 
        let c = fs::read_to_string(p).unwrap();
        if let Ok(cfg) = serde_json::from_str::<ServerConfig>(&c) {
            headers.insert(header::AUTHORIZATION, header::HeaderValue::from_str(&cfg.token).unwrap());
            headers.insert(
                header::AUTHORIZATION,
                header::HeaderValue::from_str(&cfg.token).unwrap(),
            );
        }

    } else {
        println!("No config file found! You will need to authenticate with the `login` subcommand"); 
        println!("No config file found! You will need to authenticate with the `login` subcommand");
    }

    let client = reqwest::blocking::ClientBuilder::new().default_headers(headers).build().unwrap();
    let client = reqwest::blocking::ClientBuilder::new()
        .default_headers(headers)
        .build()
        .unwrap();

    let root = args.server.clone();

    match args.action {
        Action::List => {
            list(args, client).unwrap();
        },
        }
        Action::Stop { uuid, force } => {
            stop(root, client, uuid, force).unwrap();
        },
        }
        Action::Start { uuid } => {
            start(root, client, uuid).unwrap();
        },
        }
        Action::Pause { uuid } => {
            pause(root, client, uuid).unwrap();
        },
        }
        Action::Reboot { uuid, force } => {
            reboot(root, client, uuid, force).unwrap();
        }
        Action::View { uuid } => {
            view(root, uuid).unwrap();
        },
        Action::Create { max_mem, max_cpus, disk_size_mb, name, ship } => {
        }
        Action::Create {
            max_mem,
            max_cpus,
            disk_size_mb,
            name,
            ship,
        } => {
            create(root, client, max_mem, max_cpus, disk_size_mb, name, ship).unwrap();
        },
        }
        Action::Login { key } => {
            login(root, client, key);
        }
    };
}


fn list(a: Args, c: Client) -> Result<(), CliError> {
    let res: Vec<Planet> = c.get(format!("http://{}/planets/list", a.server)).send()?.json()?;
    let res: Vec<Planet> = c
        .get(format!("http://{}/planets/list", a.server))
        .send()?
        .json()?;

    let mut table = Table::new("{:<} | {:<} | {:<} | {:<}");

    table.add_row(row!(
            "name", "uuid", "status", "running")
        );
    table.add_row(row!(
            "----", "----", "------", "-------")
        );
    table.add_row(row!("name", "uuid", "status", "running"));
    table.add_row(row!("----", "----", "------", "-------"));

    for p in res {
        table.add_row(row!(
                &p.name,
                &p.uuid,
                &p.status,
                if p.orbiting { "yes" } else { "no" },
                ));
            &p.name,
            &p.uuid,
            &p.status,
            if p.orbiting { "yes" } else { "no" },
        ));
    }

    println!("{}", table);


@@ 163,40 172,50 @@ fn stop(server: String, c: Client, u: String, f: bool) -> Result<(), CliError> {
    match res.status() {
        StatusCode::OK => {
            println!("Stopped.");
        },
        }
        _ => {
            return Err(CliError::Cli(format!("Could not stop VM: {}", res.text()?)));
        },
        }
    };

    Ok(())
}

fn start(server: String, c: Client, u: String) -> Result<(), CliError> {
    let res = c.post(format!("http://{}/planets/{}/start", server, u)).send()?;
    let res = c
        .post(format!("http://{}/planets/{}/start", server, u))
        .send()?;

    match res.status() {
        StatusCode::OK => {
            println!("Started.");
        },
        }
        _ => {
            return Err(CliError::Cli(format!("Could not start VM: {}", res.text()?)));
        },
            return Err(CliError::Cli(format!(
                "Could not start VM: {}",
                res.text()?
            )));
        }
    };

    Ok(())
}

fn pause(server: String, c: Client, u: String) -> Result<(), CliError> {
    let res = c.post(format!("http://{}/planets/{}/pause", server, u)).send()?;
    let res = c
        .post(format!("http://{}/planets/{}/pause", server, u))
        .send()?;

    match res.status() {
        StatusCode::OK => {
            println!("Paused.");
        },
        }
        _ => {
            return Err(CliError::Cli(format!("Could not pause VM: {}", res.text()?)));
        },
            return Err(CliError::Cli(format!(
                "Could not pause VM: {}",
                res.text()?
            )));
        }
    };

    Ok(())


@@ 212,10 231,13 @@ fn reboot(server: String, c: Client, u: String, f: bool) -> Result<(), CliError>
    match res.status() {
        StatusCode::OK => {
            println!("Rebooted.");
        },
        }
        _ => {
            return Err(CliError::Cli(format!("Could not reboot VM: {}", res.text()?)));
        },
            return Err(CliError::Cli(format!(
                "Could not reboot VM: {}",
                res.text()?
            )));
        }
    };

    Ok(())


@@ 229,14 251,23 @@ fn view(server: String, u: String) -> Result<(), CliError> {
        .arg("-c")
        .arg(qemu_url)
        .arg(u)
        .output() {
            println!("Could not run virt-viewer: {}", e);
        }
        .output()
    {
        println!("Could not run virt-viewer: {}", e);
    }

    Ok(())
}

fn create(s: String, c: Client, mem: u64, cpus: u64, disk_size: u64, name: String, ship: String) -> Result<(), CliError> {
fn create(
    s: String,
    c: Client,
    mem: u64,
    cpus: u64,
    disk_size: u64,
    name: String,
    ship: String,
) -> Result<(), CliError> {
    let url = format!("http://{}/planets/new", s);

    let new_p: NewPlanet = NewPlanet {


@@ 244,7 275,7 @@ fn create(s: String, c: Client, mem: u64, cpus: u64, disk_size: u64, name: Strin
        ship,
        disk_size_mb: disk_size,
        max_mem: Memory(mem),
        max_cpus: CpuCount(cpus)
        max_cpus: CpuCount(cpus),
    };

    println!("Creating new planet...");


@@ 256,9 287,12 @@ fn create(s: String, c: Client, mem: u64, cpus: u64, disk_size: u64, name: Strin
            let js: Planet = res.json()?;

            println!("Created. UUID: {}", js.uuid);
        },
        }
        _ => {
            return Err(CliError::Cli(format!("Could not create VM: {}", res.text()?)));
            return Err(CliError::Cli(format!(
                "Could not create VM: {}",
                res.text()?
            )));
        }
    };



@@ 274,27 308,24 @@ fn login(s: String, c: Client, k: String) -> Result<(), CliError> {
    match res.status() {
        StatusCode::OK => {
            let token = res.text()?;
            let xdg_dirs = xdg::BaseDirectories::with_profile("solarctl", s).unwrap(); 

    if let Some(p) = xdg_dirs.find_config_file("config.json") {
        let cfg = ServerConfig {
            token
        };
            let xdg_dirs = xdg::BaseDirectories::with_profile("solarctl", s).unwrap();

        fs::write(p, serde_json::to_string_pretty(&cfg).unwrap()).unwrap();
            if let Some(p) = xdg_dirs.find_config_file("config.json") {
                let cfg = ServerConfig { token };

    } else {
        let p = xdg_dirs.place_config_file("config.json").unwrap();
        let cfg = ServerConfig {
            token
        };
                fs::write(p, serde_json::to_string_pretty(&cfg).unwrap()).unwrap();
            } else {
                let p = xdg_dirs.place_config_file("config.json").unwrap();
                let cfg = ServerConfig { token };

        fs::write(p, serde_json::to_string_pretty(&cfg).unwrap()).unwrap();
    }

        },
                fs::write(p, serde_json::to_string_pretty(&cfg).unwrap()).unwrap();
            }
        }
        _ => {
            return Err(CliError::Cli(format!("Could not authenticate: {}", res.text()?)));
            return Err(CliError::Cli(format!(
                "Could not authenticate: {}",
                res.text()?
            )));
        }
    };