#!/usr/bin/env ruby require_relative "../lib/harbr" class HarbrCLI < Thor no_commands do def display_containers_table(containers) return puts "No containers available." if containers.empty? # Define headers based on Container attributes headers = ["Name", "Host Header", "IP", "Port"] rows = containers.map do |container| [container.name, container.host_header, container.ip.nil? ? "127.0.0.1" : container.ip, container.port] end table = ::Terminal::Table.new(headings: headers, rows: rows) puts "" puts "Harbr Containers" puts "" puts table puts "" end def check_and_create_directory(path) unless Dir.exist?(path) puts "Creating directory: #{path}" FileUtils.mkdir_p(path) end end def command_exists?(command) system("command -v #{command} > /dev/null 2>&1") end def install_with_snap(package) puts "Installing #{package} using Snap..." system("sudo snap install #{package}") or raise "Failed to install #{package}" end def install_with_apt(package) puts "Installing #{package} using apt..." system("sudo apt install #{package}") or raise "Failed to install #{package}" end def scan_for_containers Dir.glob("/var/harbr/containers/*").each_with_object({}) do |container_path, hash| next unless File.directory?(container_path) container_name = File.basename(container_path) versions = Dir.glob("#{container_path}/versions/*").select { |path| File.directory?(path) } versions.each { |version_path| hash["#{container_name}/versions/#{File.basename(version_path)}"] = true } end end PLACED_FILE = "/var/harbr/.placed" def place(container, version) placed_containers = File.exist?(PLACED_FILE) ? File.read(PLACED_FILE).split("\n") : [] if placed_containers.include?("#{container}/#{version}") puts "Container '#{container}', Version '#{version}' is already placed." else puts "Placing container: '#{container}', Version: '#{version}'" run_jobs(container, version) File.open(PLACED_FILE, "a") { |file| file.puts("#{container}/#{version}") } end end def run_jobs(container, version) puts "Running tasks for container: #{container}, version: #{version}" puts "waiting for 3 seconds" sleep 3 puts "done waiting" if version.to_i == 0 puts "Initial Deploy!!! of #{container}" Harbr::Job.perform_async(container, version) else Harbr::Next::Job.perform_async(container, version) puts "deploy next version #{version} of #{container}" end end def create_traefik_config(containers) `rm -rf "/etc/traefik/harbr.toml"` config = { "http" => { "routers" => { "traefik-dashboard" => { "rule" => "Host(`traefik.harbr.zero2one.ee`)", "service" => "api@internal" } }, "services" => {} } } containers.each do |container| container.ip = "127.0.0.1" config["http"]["routers"]["#{container.name}-router"] = { "rule" => "Host(`#{container.host_header}`)", "service" => "#{container.name}-service" } config["http"]["services"]["#{container.name}-service"] = { "loadBalancer" => { "servers" => [{"url" => "http://#{container.ip}:#{container.port}"}] } } end File.write("/etc/traefik/harbr.toml", TomlRB.dump(config)) puts "Traefik configuration written to /etc/traefik/harbr.toml" end end desc "destroy", "Destroy a container and remove all traces" def destroy(container_name) puts "Destroying container: #{container_name}" puts `sv stop #{container_name}` container_repo = Harbr::Container::Repository.new container = container_repo.find_by_name(container_name) if container puts `port release #{container.port}` create_traefik_config(container_repo.all) `rm -rf /var/harbr/#{container_name}` `rm -rf /etc/service/#{container_name}` `rm -rf /etc/sv/harbr/#{container_name}` `rm -rf /var/log/harbr/#{container_name}` `lsof -i :#{container.port} | awk 'NR!=1 {print $2}' | xargs kill` container_repo.delete(container) puts "Container #{container_name} destroyed successfully." else puts "Container #{container_name} not found." end end desc "logs CONTAINER", "Show logs for a given container" def logs(container) container_repo = Harbr::Container::Repository.new container = container_repo.find_by_name(container) if container exec "tail -f /var/log/harbr/#{container.name}/current" else puts "Container not recognized" end end desc "setup", "Set up Harbr environment" def setup # Check and create /var/harbr directory check_and_create_directory("/var/harbr/containers") # Check for Ruby, Traefik, and runit, and install if necessary install_with_snap("ruby") unless command_exists?("ruby") install_with_snap("traefik") unless command_exists?("traefik") install_with_apt("runit") unless command_exists?("runit") puts "Setup completed successfully." end desc "containers", "show all containers" def containers container_repo = Harbr::Container::Repository.new display_containers_table(container_repo.all) end desc "monitor", "Monitor /var/harbr/comtainers for new container versions" def monitor puts "Monitoring /var/harbr/containers for new container versions..." last_known_state = {} loop do current_state = scan_for_containers new_versions = current_state.keys - last_known_state.keys new_versions.each do |container_version| container, version = container_version.split("/versions/") place(container, version) end last_known_state = current_state sleep 3 # Poll every 10 seconds end end end HarbrCLI.start(ARGV)