Skip to content

Commit

Permalink
Merge pull request #7 from RukuLab/dev
Browse files Browse the repository at this point in the history
Feature: Ruku configuration file
  • Loading branch information
Joker666 authored Aug 7, 2024
2 parents 60b7a45 + 998d1bb commit fcad118
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 54 deletions.
59 changes: 42 additions & 17 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ bollard = "0.17.0"
clap = { version = "4.5.13", features = ["derive"] }
cmd_lib = "1.9.4"
colored = "2.1.0"
dotenvy = "0.15.7"
envy = "0.4.2"
home = "0.5.9"
nixpacks = "1.26.0"
port-selector = "0.1.6"
serde = { version = "1.0.204", features = ["derive"] }
serde_yaml = "0.9.34"
tokio = { version = "1.39.2", features = ["rt", "rt-multi-thread", "macros"] }
validator = { version = "0.18.1", features = ["derive"] }
15 changes: 8 additions & 7 deletions src/container.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
use std::collections::HashMap;
use std::str::FromStr;

use bollard::container::{CreateContainerOptions, ListContainersOptions, StartContainerOptions};
use bollard::models::{
ContainerCreateResponse, ContainerStateStatusEnum, ContainerSummary, HostConfig, PortBinding, PortMap,
};
use bollard::Docker;
use std::collections::HashMap;
use std::str::FromStr;

use crate::logger::Logger;
use crate::misc::get_image_name_with_version;
use crate::model::Config;
use crate::model::RukuConfig;

pub struct Container<'a> {
log: &'a Logger,
name: &'a str,
docker: &'a Docker,
config: &'a Config,
config: &'a RukuConfig,
}

impl<'a> Container<'a> {
pub fn new(log: &'a Logger, name: &'a str, docker: &'a Docker, config: &'a Config) -> Container<'a> {
pub fn new(log: &'a Logger, name: &'a str, docker: &'a Docker, config: &'a RukuConfig) -> Container<'a> {
Container {
log,
name,
Expand Down Expand Up @@ -133,14 +134,14 @@ impl<'a> Container<'a> {
platform: None,
};

let exposed_port = format!("{}/tcp", self.config.port.unwrap());
let exposed_port = format!("{}/tcp", self.config.port);
let mut host_config = HostConfig::default();
let mut port_bindings = PortMap::new();
port_bindings.insert(
exposed_port.clone(),
Some(vec![PortBinding {
host_ip: None,
host_port: Some(self.config.port.unwrap().to_string()),
host_port: Some(self.config.port.to_string()),
}]),
);
host_config.port_bindings = Some(port_bindings);
Expand Down
14 changes: 5 additions & 9 deletions src/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ use nixpacks::nixpacks::plan::{generator::GeneratePlanOptions, BuildPlan};
use crate::container::Container;
use crate::logger::Logger;
use crate::misc::get_image_name_with_version;
use crate::model::Config;
use crate::model::RukuConfig;

pub struct Deploy<'a> {
log: &'a Logger,
name: &'a str,
path: &'a str,
config: &'a Config,
config: &'a RukuConfig,
container: &'a Container<'a>,
}

Expand All @@ -20,7 +20,7 @@ impl<'a> Deploy<'a> {
log: &'a Logger,
name: &'a str,
path: &'a str,
config: &'a Config,
config: &'a RukuConfig,
container: &'a Container<'a>,
) -> Deploy<'a> {
Deploy {
Expand All @@ -33,11 +33,6 @@ impl<'a> Deploy<'a> {
}

pub async fn run(&self) {
if self.config.port.is_none() {
self.log.error("No port specified, skipping deployment");
std::process::exit(1);
}

self.log.step(&format!("Running from {}", self.path));

// Nix pack
Expand Down Expand Up @@ -77,7 +72,8 @@ impl<'a> Deploy<'a> {
create_docker_image(self.path, envs, &options, &build_options)
.await
.unwrap_or_else(|e| {
self.log.error(&format!("Error creating image: {}", e));
self.log
.error(&format!("Error creating Docker image at path {}: {}", self.path, e));
std::process::exit(1);
});

Expand Down
69 changes: 53 additions & 16 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use std::fs;

use bollard::Docker;
use clap::{Parser, Subcommand};
use validator::Validate;

use logger::Logger;
use server_config::ServerConfig;
Expand All @@ -8,7 +11,7 @@ use crate::container::Container;
use crate::deploy::Deploy;
use crate::git::Git;
use crate::misc::sanitize_app_name;
use crate::model::Config;
use crate::model::RukuConfig;

mod container;
mod deploy;
Expand All @@ -22,11 +25,12 @@ mod server_config;
#[command(version, about = "A CLI app for managing your server.")]
struct Cli {
#[command(subcommand)]
command: Commands,
command: Command,
}

/// Enum representing the various commands that can be executed by the CLI.
#[derive(Subcommand)]
enum Commands {
enum Command {
/// Show logs
Logs,
/// Set a configuration variable, e.g, VAR=12
Expand Down Expand Up @@ -82,10 +86,10 @@ async fn main() {
let cli = Cli::parse();

match &cli.command {
Commands::Logs => {
Command::Logs => {
println!("Showing logs...");
}
Commands::ConfigSet { var } => {
Command::ConfigSet { var } => {
println!("Setting configuration: {}", var);
// Parse `var` into key and value
let parts: Vec<&str> = var.split('=').collect();
Expand All @@ -97,39 +101,40 @@ async fn main() {
log.error("Invalid format. Use KEY=VALUE");
}
}
Commands::ConfigGet { key } => {
Command::ConfigGet { key } => {
println!("Getting configuration for: {}", key);
}
Commands::Run => {
Command::Run => {
log.section("Running application");
}
Commands::Deploy => {
Command::Deploy => {
log.section("Starting deployment");
}
Commands::Stop => {
Command::Stop => {
log.section("Stopping application...");
}
Commands::Destroy => {
Command::Destroy => {
println!("Destroying application...");
}
Commands::GitHook { repo } => {
Command::GitHook { repo } => {
git.cmd_git_hook(repo);
deploy(&log, repo, server_config).await;
deploy(&log, repo, &server_config).await;
}
Commands::GitReceivePack { repo } => {
Command::GitReceivePack { repo } => {
log.section("... RUKU ...");
let _ = get_ruku_config(&log, repo, &server_config);
git.cmd_git_receive_pack(repo);
}
Commands::GitUploadPack { repo } => {
Command::GitUploadPack { repo } => {
log.section("... RUKU ...");
git.cmd_git_upload_pack(repo);
}
}
}

async fn deploy(log: &Logger, repo: &str, server_config: ServerConfig) {
async fn deploy(log: &Logger, repo: &str, server_config: &ServerConfig) {
log.section("Deploying application");
let config = Config::default();
let config = get_ruku_config(log, repo, server_config);
let docker = get_docker(log).await;

let app = sanitize_app_name(repo);
Expand Down Expand Up @@ -163,3 +168,35 @@ async fn load_docker(log: &Logger) -> Docker {
std::process::exit(1);
})
}

fn get_ruku_config(log: &Logger, repo: &str, server_config: &ServerConfig) -> RukuConfig {
let repo_path = server_config.apps_root.join(&repo);

// Check for the presence of ruku.yml file
let config_path = repo_path.join("ruku.yml");
if !config_path.exists() {
log.error("ruku.yml file is missing in the repository");
std::process::exit(1);
}

// Parse the ruku.yml file
let config_content = fs::read_to_string(&config_path).unwrap_or_else(|e| {
log.error(&format!("Error reading ruku.yml file: {}", e));
std::process::exit(1);
});

let config: RukuConfig = serde_yaml::from_str(&config_content).unwrap_or_else(|e| {
log.error(&format!("Error parsing ruku.yml file: {}", e));
std::process::exit(1);
});

match config.validate() {
Ok(_) => (),
Err(e) => {
log.error(&format!("Error validating ruku.yml file: {}", e));
std::process::exit(1);
}
};

config
}
17 changes: 14 additions & 3 deletions src/model.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
use port_selector::is_free;
use serde::Deserialize;
use validator::{Validate, ValidationError};

#[derive(Default, Deserialize)]
pub struct Config {
pub port: Option<u16>,
#[derive(Debug, Validate, Deserialize)]
pub struct RukuConfig {
#[validate(range(min = 1024, max = 65535), custom(function = "validate_port"))]
pub port: u16,
#[validate(length(min = 1, max = 20))]
pub version: Option<String>,
}

fn validate_port(port: u16) -> Result<(), ValidationError> {
if !is_free(port) {
return Err(ValidationError::new("port is already in use"));
}
Ok(())
}

0 comments on commit fcad118

Please sign in to comment.