#!/usr/bin/env ruby require_relative "../lib/harbr" class Host < Thor attr_reader :containers attr_reader :hosts def initialize(a, b, c) @containers = Harbr::Container::Repository.new @hosts = Harbr::Host::Repository.new super(a, b, c) end no_commands do def display_table(hosts) return puts "No hosts available." if hosts.empty? # Define headers based on Container attributes headers = ["Uid", "Container","Ip","Port", "Host"] rows = hosts.map do |host| container = containers.get(host.container_uid) [host.uid, container.name,container.ip,host.port,host.header] end table = ::Terminal::Table.new(headings: headers, rows: rows) puts "" puts "Harbr Container Hosts" puts "" puts table puts "" end end desc "add CONTAINER PORT HOST", "Add a new host" def add(container,port, header) container = containers.find_by_name(container) host = Harbr::Host.new host.container_uid = container.uid host.port = port host.header = header hosts.create(host) end desc "delete ID", "Delete a host" def delete(id) host = hosts.get(id) host ||= hosts.find_by_header(id) hosts.delete(host) if host end desc "update ID HOST_HEADER", "Update a host" def update(id, host_header, port) host = hosts.get(id) host.header = host_header host.port = port hosts.update(host) end desc "list", "List all hosts" def list display_table(hosts.all) end desc "show", "show host for container" def show(name) container = containers.find_by_name(name) display_table(hosts.get_by_container(container)) end end class Container < Thor no_commands do def display_table(containers) return puts "No containers available." if containers.empty? # Define headers based on Container attributes headers = ["Uid","Name", "IP", "Host Headers and Port"] rows = containers.map do |container| raise "oops" if container.is_a?(Harbr::Host) container&.hosts = hosts&.get_by_container(container) [container.uid,container.name, container.ip, container&.hosts&.map(&:to_s)&.join("\n")] end table = ::Terminal::Table.new(headings: headers, rows: rows) puts "" puts "Harbr Containers" puts "" puts table puts "" end end attr_reader :containers attr_reader :hosts def initialize(a, b, c) @containers = Harbr::Container::Repository.new @hosts = Harbr::Host::Repository.new super(a, b, c) end desc "add NAME, IP", "Add a new container" def add(name,ip) container = Harbr::Container.new container.name = name container.ip = ip containers.create(container) end desc "delete NAME", "Delete a container" def delete(name) container = @containers.get(name) container = @containers.find_by_name(name) if container.nil? if container hosts.get_by_container(container).each do |host| hosts.delete(host) end containers.delete(container) end end desc "update NAME ID", "Update ip address of a container" def update(name, ip, port) container = @containers.find_by_name(name) container.ip = ip §§container.port = port @containers.update(container) if container end desc "list", "List all names" def list display_table(@containers.all) end desc "show", "show container" def show(name) container = @containers.find_by_name(name) display_table([container]) end end class HarbrCLI < Thor include Thor::Actions include Harbr::SSH attr_reader :containers attr_reader :hosts def initialize(a, b, c) @containers = Harbr::Container::Repository.new @hosts = Harbr::Host::Repository.new super(a, b, c) end no_commands do def render_hosts(container) container&.hosts&.map { |host| "Host(`#{host.header}`)" }&.join(" || ") end def commit_to_file(container) container.hosts.each do |host| filename,contents = generate_traefik_config_with_filename(host.header, container.ip, host.port) on_ssh do |ssh| ssh << ("echo '#{contents}' > /etc/traefik/providers/#{filename}") end end end def generate_traefik_config_with_filename(domain, host, port) server_url = "http://#{host}:#{port}" service_name = "#{domain.gsub('.', '_')}_#{port}" filename = "#{domain.gsub('.', '-')}.toml" erb_template = <<-ERB # Traefik Configuration for <%= domain %> # Service for <%= domain %> # produced by Harbr version #{Harbr::VERSION} # Router for <%= domain %> [http.routers.<%= domain.gsub('.', '-') %>] rule = "Host(`<%= domain %>`)" service = "<%= service_name %>" entryPoints = ["http", "https"] [http.routers.<%= domain.gsub('.', '-') %>.tls] certResolver = "letsencrypt" # Service for <%= domain %> [http.services.<%= service_name %>.loadBalancer] [[http.services.<%= service_name %>.loadBalancer.servers]] url = "<%= server_url %>" ERB config_content = ERB.new(erb_template, trim_mode: '-').result(binding) return filename, config_content end 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 command_exists?(command) system("command -v #{command} > /dev/null 2>&1") end def write_to_file(path, contents) dirname = File.dirname(path) FileUtils.mkdir_p(dirname) unless File.directory?(dirname) File.write(path, contents) 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 end desc "setup", "Set up Harbr environment" def setup # Check and create /var/harbr directory check_and_create_directory("/var/harbr/containers") check_and_create_directory("/var/dddr") # 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") install_with_apt("lxd") unless command_exists?("lxd") install_with_apt("lxc") unless command_exists?("lxc") puts "Setup completed successfully." end desc "commit", "commit changes traeffic" def commit on_ssh do |ssh| ssh << "rm -rf /etc/traefik/providers/*;" end containers.all.each do |container| container.hosts = hosts.get_by_container(container) commit_to_file(container) end end desc "rollback", "rollback changes to traeffic" def rollback end desc "update", "update to the latest version of harbr" def update system "gem update harbr" end # Registering the Host subcommand desc "host", "Manage hosts" subcommand "host", Host # Registering the Host subcommand desc "container", "Manage containers" subcommand "container", Container end HarbrCLI.start(ARGV)