class Procsd::CLI
Public Instance Methods
__exec(process_name)
click to toggle source
# File lib/procsd/cli.rb, line 248 def __exec(process_name) preload! start_cmd = @config[:processes].dig(process_name, "commands", "ExecStart") raise ArgumentError, "Process is not defined: #{process_name}" unless start_cmd process_env = @config[:environment].each { |k, v| @config[:environment][k] = v.to_s } if options["dev"] dev_env = @config[:dev_environment].each { |k, v| @config[:dev_environment][k] = v.to_s } process_env.merge!(dev_env) end exec process_env, start_cmd end
__print_version()
click to toggle source
# File lib/procsd/cli.rb, line 265 def __print_version puts VERSION end
config(name)
click to toggle source
# File lib/procsd/cli.rb, line 219 def config(name) preload! options = { "user" => ENV["USER"], "dir" => ENV["PWD"], "path" => `/bin/bash -ilc 'echo $PATH'`.strip } generator = Generator.new(@config, options) case name when "sudoers" puts generator.generate_sudoers(options["user"], has_reload: has_reload?) when "services" return unless valid_create_options?(options) services = generator.generate_units services.each do |service_name, service_data| puts "Service: #{service_name} (size: #{service_data[:size]}):" puts "---\n\n" puts service_data[:content] puts "---\n\n" end when "certbot_command" puts get_certbot_command.join(' ') else raise ArgumentError, "Wrong type of argument: #{name}" end end
create()
click to toggle source
# File lib/procsd/cli.rb, line 15 def create raise ConfigurationError, "Can't find systemctl executable available" unless in_path?("systemctl") preload! if @config[:nginx] raise ConfigurationError, "Can't find nginx executable available" unless in_path?("nginx") public_folder_path = @config[:nginx]["public_folder_path"] || "public" unless Dir.exist?(File.join options["dir"], public_folder_path) raise ConfigurationError, "Missing public folder path to use with Nginx" end unless @config.dig(:environment, "PORT") raise ConfigurationError, "Please provide PORT environment variable in procsd.yml to use with Nginx" end if @config[:nginx]["ssl"] raise ConfigurationError, "Can't find certbot executable available" unless in_path?("certbot") end end if !target_exist? perform_create else if options["or-restart"] restart else say("App target `#{target_name}` already exists", :red) end end end
destroy()
click to toggle source
# File lib/procsd/cli.rb, line 48 def destroy preload! if target_exist? stop disable units.each do |filename| path = File.join(systemd_dir, filename) execute %W(sudo rm #{path}) and say "Deleted: #{path}" if File.exist?(path) end if execute %w(sudo systemctl daemon-reload) say("Reloaded configuraion (daemon-reload)", :green) end say("App services were stopped, disabled and removed", :green) sudoers_file_path = "#{SUDOERS_DIR}/#{app_name}" if system "sudo", "test", "-e", sudoers_file_path say("Sudoers file removed", :green) if execute %W(sudo rm #{sudoers_file_path}) end if @config[:nginx] enabled_path = File.join(NGINX_DIR, "sites-enabled", app_name) available_path = File.join(NGINX_DIR, "sites-available", app_name) [enabled_path, available_path].each do |path| execute %W(sudo rm #{path}) and say "Deleted: #{path}" if File.exist?(path) end execute %w(sudo systemctl reload-or-restart nginx) say("Nginx config removed and daemon reloaded", :green) end else say_target_not_exists end end
disable()
click to toggle source
# File lib/procsd/cli.rb, line 97 def disable preload! say_target_not_exists and return unless target_exist? say "Note: app target #{target_name} already disabled" if !target_enabled? if execute %W(sudo systemctl disable #{target_name}) say("Disabled app target #{target_name}", :green) end end
enable()
click to toggle source
# File lib/procsd/cli.rb, line 86 def enable preload! say_target_not_exists and return unless target_exist? say "Note: app target #{target_name} already enabled" if target_enabled? if execute %W(sudo systemctl enable #{target_name}) say("Enabled app target #{target_name}", :green) end end
list()
click to toggle source
# File lib/procsd/cli.rb, line 210 def list preload! say_target_not_exists and return unless target_exist? command = %W(systemctl list-dependencies #{target_name}) execute command, type: :exec end
logs(service_name = nil)
click to toggle source
# File lib/procsd/cli.rb, line 195 def logs(service_name = nil) preload! command = %w(journalctl --no-pager --no-hostname --all --output short-iso) command.push("-n", options.fetch("num", "100")) command.push("-f") if options["tail"] command.push("--system") if options["system"] command.push("--priority", options["priority"]) if options["priority"] command.push("--grep", "'" + options["grep"] + "'") if options["grep"] command.push("--unit", "#{app_name}-#{service_name}*") execute command, type: :exec end
restart(service_name = nil)
click to toggle source
# File lib/procsd/cli.rb, line 146 def restart(service_name = nil) preload! say_target_not_exists and return unless target_exist? if service_name full_name = to_full_name(service_name) if execute %W(sudo systemctl reload-or-restart #{full_name} --all) say("Restarted app service (#{full_name})", :green) end else # If one of the child services of a target has `ExecReload` and `ReloadPropagatedFrom` # options defined, then use `reload-or-restart` to call all services (not the main target) # because of systemd bug https://github.com/systemd/systemd/issues/10638 success = if has_reload? execute %W(sudo systemctl reload-or-restart #{app_name}-* --all) else execute %W(sudo systemctl restart #{target_name}) end if success say("Restarted app services (#{target_name})", :green) end end end
start(service_name = nil)
click to toggle source
# File lib/procsd/cli.rb, line 108 def start(service_name = nil) preload! say_target_not_exists and return unless target_exist? if service_name full_name = to_full_name(service_name) say "Note: app service #{full_name} already started/active" if service_active?(full_name) if execute %W(sudo systemctl start #{full_name} --all) say("Started app service (#{full_name})", :green) end else say "Note: app target #{target_name} already started/active" if target_active? if execute %W(sudo systemctl start #{target_name}) say("Started app services (#{target_name})", :green) end end end
status(service_name = nil)
click to toggle source
# File lib/procsd/cli.rb, line 175 def status(service_name = nil) preload! say_target_not_exists and return unless target_exist? if options["short"] command = %w(systemctl list-units --no-pager --no-legend --all) else command = %w(systemctl status --no-pager --output short-iso --all) end command << (options["target"] ? target_name : to_full_name(service_name)) execute command, type: :exec end
stop(service_name = nil)
click to toggle source
# File lib/procsd/cli.rb, line 127 def stop(service_name = nil) preload! say_target_not_exists and return unless target_exist? if service_name full_name = to_full_name(service_name) say "Note: app service #{full_name} already stopped/inactive" if !service_active?(full_name) if execute %W(sudo systemctl stop #{full_name} --all) say("Stopped app service (#{full_name})", :green) end else say "Note: app target #{target_name} already stopped/inactive" if !target_active? if execute %W(sudo systemctl stop #{target_name}) say("Stopped app services (#{target_name})", :green) end end end
Private Instance Methods
app_name()
click to toggle source
# File lib/procsd/cli.rb, line 414 def app_name @config[:app] end
execute(command, type: :system)
click to toggle source
# File lib/procsd/cli.rb, line 372 def execute(command, type: :system) say("Execute: #{command.join(' ')}", :yellow) if ENV["VERBOSE"] == "true" case type when :system system *command when :exec exec *command end end
get_certbot_command()
click to toggle source
# File lib/procsd/cli.rb, line 341 def get_certbot_command command = %w(sudo certbot --agree-tos --no-eff-email --redirect --non-interactive --nginx) @config[:nginx]["server_name"].split(" ").map(&:strip).each do |domain| command.push("-d", domain) end if email = ENV["CERTBOT_EMAIL"] command.push("--email", email) else command << "--register-unsafely-without-email" end end
has_reload?()
click to toggle source
# File lib/procsd/cli.rb, line 359 def has_reload? @config[:processes].any? { |name, values| values.dig("commands", "ExecReload") } end
in_path?(name)
click to toggle source
# File lib/procsd/cli.rb, line 355 def in_path?(name) system("which", name, [:out, :err] => "/dev/null") end
perform_create()
click to toggle source
# File lib/procsd/cli.rb, line 285 def perform_create return unless valid_create_options?(options) generator = Generator.new(@config, options) generator.generate_units(save: true) if execute %w(sudo systemctl daemon-reload) say("Reloaded configuraion (daemon-reload)", :green) end enable if options["or-restart"] start say("App services were created, enabled and started", :green) else say("App services were created and enabled. Run `start` to start them", :green) end if options["add-to-sudoers"] if Dir.exist?(SUDOERS_DIR) if generator.generate_sudoers(options["user"], has_reload: has_reload?, save: true) say("Sudoers file #{SUDOERS_DIR}/#{app_name} was created", :green) end else say("Directory #{SUDOERS_DIR} does not exists, sudoers file wasn't created", :red) end else say "Note: add following line to the sudoers file (`$ sudo visudo`) if you don't " \ "want to type password each time for start/stop/restart commands:" puts generator.generate_sudoers(options["user"], has_reload: has_reload?) end if nginx = @config[:nginx] generator.generate_nginx_conf(save: true) say("Nginx config created", :green) # Reference: https://certbot.eff.org/docs/using.html#certbot-command-line-options # How it works in Caddy https://caddyserver.com/docs/automatic-https if nginx["ssl"] certbot_command = get_certbot_command say "Trying to obtain SSL certificate for Nginx config using Certbot..." if execute certbot_command say("Successfully installed SSL cert using Certbot", :green) else msg = "Failed to install SSL cert using Certbot. Make sure that all provided domains are pointing to this server IP." say(msg, :red) end end if execute %w(sudo systemctl reload-or-restart nginx) say("Nginx daemon reloaded", :green) end end end
preload!()
click to toggle source
# File lib/procsd/cli.rb, line 418 def preload! @config = { processes: {}} raise ConfigurationError, "Config file procsd.yml doesn't exists" unless File.exist? "procsd.yml" begin procsd = YAML.load(ERB.new(File.read "procsd.yml").result) rescue => e raise ConfigurationError, "Can't read procsd.yml: #{e.inspect}" end raise ConfigurationError, "Missing app name in the procsd.yml file" unless procsd["app"] @config[:app] = procsd["app"] # If procsd.yml doesn't contains processes defined, try to read Procfile unless procsd["processes"] msg = "Procfile doesn't exists. Define processes in procsd.yml or create Procfile" raise ConfigurationError, msg unless File.exist? "Procfile" begin procfile = YAML.load_file("Procfile") rescue => e raise ConfigurationError, "Can't read Procfile: #{e.inspect}" end end if procsd["formation"] formation = procsd["formation"].split(",").map { |f| f.split("=") }.to_h formation.each { |k, v| formation[k] = v.to_i } else formation = {} end processes = procsd["processes"] || procfile processes.each do |process_name, opts| if opts.kind_of?(Hash) raise ConfigurationError, "Missing ExecStart command for `#{process_name}` process" unless opts["ExecStart"] @config[:processes][process_name] = { "commands" => opts } else @config[:processes][process_name] = { "commands" => { "ExecStart" => opts }} end @config[:processes][process_name]["size"] = formation[process_name] || 1 end @config[:environment] = procsd["environment"] || {} @config[:dev_environment] = procsd["dev_environment"] || {} @config[:systemd_dir] = procsd["systemd_dir"] || DEFAULT_SYSTEMD_DIR @config[:nginx] = procsd["nginx"] end
say_target_not_exists()
click to toggle source
# File lib/procsd/cli.rb, line 382 def say_target_not_exists say("App target #{target_name} is not exists", :red) end
service_active?(service_name)
click to toggle source
# File lib/procsd/cli.rb, line 402 def service_active?(service_name) system "systemctl", "is-active", "--quiet", service_name end
systemd_dir()
click to toggle source
# File lib/procsd/cli.rb, line 390 def systemd_dir @config[:systemd_dir] end
target_active?()
click to toggle source
# File lib/procsd/cli.rb, line 398 def target_active? system "systemctl", "is-active", "--quiet", target_name end
target_enabled?()
click to toggle source
# File lib/procsd/cli.rb, line 394 def target_enabled? system "systemctl", "is-enabled", "--quiet", target_name end
target_exist?()
click to toggle source
# File lib/procsd/cli.rb, line 386 def target_exist? File.exist?(File.join systemd_dir, target_name) end
target_name()
click to toggle source
# File lib/procsd/cli.rb, line 406 def target_name "#{app_name}.target" end
to_full_name(service_name)
click to toggle source
# File lib/procsd/cli.rb, line 410 def to_full_name(service_name) "#{app_name}-#{service_name}*" end
units()
click to toggle source
# File lib/procsd/cli.rb, line 363 def units all = [target_name] @config[:processes].each do |name, values| values["size"].times { |i| all << "#{app_name}-#{name}.#{i + 1}.service" } end all end
valid_create_options?(opts)
click to toggle source
# File lib/procsd/cli.rb, line 271 def valid_create_options?(opts) opts.each do |key, value| next unless %w(user dir path).include? key if value.nil? || value.empty? say("Can't fetch value for --#{key}, please provide it's as an argument", :red) return false else say("Value of the --#{key} option: #{value}", :yellow) end end true end