Improved UI a bit.

master
Luka Vandervelden 2019-11-09 13:35:25 +01:00
parent 32dd294c1b
commit 5852a1b818
3 changed files with 84 additions and 52 deletions

View File

@ -8,6 +8,8 @@ description: |
Services management tool. Services management tool.
dependencies: dependencies:
weird-crystal-base:
git: https://git.karchnu.fr/WeirdOS/weird-crystal-base
specparser: specparser:
git: https://git.karchnu.fr/WeirdOS/recipes-parser git: https://git.karchnu.fr/WeirdOS/recipes-parser
crinja: crinja:

View File

@ -2,10 +2,9 @@ require "option_parser"
require "yaml" require "yaml"
require "colorize" require "colorize"
require "./config.cr" require "weird-crystal-base"
# TODO: require "./config.cr"
# - Be more declarative about the definition of commands.
require "./service/*" require "./service/*"
@ -16,7 +15,13 @@ args = [] of String
force = false force = false
verbose = false verbose = false
alias Command = Proc(Array(String), Nil) class Service::Context < Weird::Base
property pid_directory = PID_DIRECTORY
property log_directory = LOG_DIRECTORY
property services_directory = RC_DIRECTORY
end
alias Command = Proc(Service::Context, Array(String), Nil)
alias CommandTuple = Tuple(String, String, Command) alias CommandTuple = Tuple(String, String, Command)
class CommandsList class CommandsList
def initialize def initialize
@ -43,7 +48,7 @@ class CommandsList
end end
commands = CommandsList.new commands = CommandsList.new
commands.push "add", "Adds a service to an environment." do |args| commands.push "add", "Adds a service to an environment." do |context, args|
providers = Hash(String, String).new providers = Hash(String, String).new
domain = nil domain = nil
ports = Hash(String, Int32).new ports = Hash(String, Int32).new
@ -136,10 +141,26 @@ commands.push "add", "Adds a service to an environment." do |args|
if service.requires_domain && !service.domain if service.requires_domain && !service.domain
raise Service::Exception.new "'#{service.name}' requires a domain= parameter to be provided!" raise Service::Exception.new "'#{service.name}' requires a domain= parameter to be provided!"
end end
context.title "Adding #{service.to_s}"
service.providers.each do |token, provider|
context.info "'#{token}' provider will be #{provider.to_s}"
end
service.port_definitions.each do |definition|
name = definition.name
context.info "Port for '#{name}' is #{service.ports[name]}"
end
if service.domain
context.info "Domain is '#{service.domain}'"
end
end.write RC_DIRECTORY end.write RC_DIRECTORY
end end
commands.push "del", "Removes a service from an environment." do |args| commands.push "del", "Removes a service from an environment." do |context, args|
if args.size < 1 if args.size < 1
STDERR.puts "usage: service del <id> [id [...]]" STDERR.puts "usage: service del <id> [id [...]]"
exit 1 exit 1
@ -150,7 +171,7 @@ commands.push "del", "Removes a service from an environment." do |args|
service = Service.get_by_id(id) service = Service.get_by_id(id)
if service.nil? if service.nil?
STDERR.puts "#{id}: no such service" context.error "#{id}: no such service"
rvalue = 1 rvalue = 1
next next
end end
@ -158,7 +179,7 @@ commands.push "del", "Removes a service from an environment." do |args|
revdeps = service.reverse_dependencies revdeps = service.reverse_dependencies
if revdeps.size > 1 && ! force if revdeps.size > 1 && ! force
STDERR.puts "#{id}: has reverse dependencies, use -f to force" context.error "#{id}: has reverse dependencies, use -f to force"
rvalue = 2 rvalue = 2
next next
end end
@ -166,20 +187,18 @@ commands.push "del", "Removes a service from an environment." do |args|
revdeps.reverse.each do |service| revdeps.reverse.each do |service|
next if ! service.running? PID_DIRECTORY next if ! service.running? PID_DIRECTORY
puts "stopping #{service.to_s}" service.stop context
service.stop PID_DIRECTORY
end end
revdeps.reverse.each do |service| revdeps.reverse.each do |service|
puts "removing #{service.to_s}" service.remove context
service.remove RC_DIRECTORY
end end
end end
exit rvalue exit rvalue
end end
commands.push "start", "Starts a service." do commands.push "start", "Starts a service." do |context, args|
services = args.map do |arg| services = args.map do |arg|
service = Service.get_by_id(arg) service = Service.get_by_id(arg)
@ -192,20 +211,12 @@ commands.push "start", "Starts a service." do
services.each do |service| services.each do |service|
service.dependency_tree.flatten.reverse.each do |service| service.dependency_tree.flatten.reverse.each do |service|
next if service.running? PID_DIRECTORY service.start context
if service.non_runnable
STDOUT << "#{service.to_s}: non runnable\n"
next
end
puts "starting #{service.to_s}"
service.start PID_DIRECTORY, LOG_DIRECTORY
end end
end end
end end
commands.push "stop", "Stops a running service." do |args| commands.push "stop", "Stops a running service." do |context, args|
services = args.map do |arg| services = args.map do |arg|
service = Service.get_by_id(arg) service = Service.get_by_id(arg)
@ -222,13 +233,12 @@ commands.push "stop", "Stops a running service." do |args|
service.reverse_dependencies.reverse.each do |service| service.reverse_dependencies.reverse.each do |service|
next if ! service.running? PID_DIRECTORY next if ! service.running? PID_DIRECTORY
puts "stopping #{service.to_s}" service.stop context
service.stop PID_DIRECTORY
end end
end end
end end
commands.push "status", "Prints the status of services." do |args| commands.push "status", "Prints the status of services." do |context, args|
ENV["SERVICE_VERBOSE"] = verbose.to_s ENV["SERVICE_VERBOSE"] = verbose.to_s
child = Process.run "#{OWN_LIBEXEC_DIR}/status", args, child = Process.run "#{OWN_LIBEXEC_DIR}/status", args,
@ -244,7 +254,7 @@ commands.push "status", "Prints the status of services." do |args|
exit return_value exit return_value
end end
commands.push "show", "Shows a service's configuration and state." do |args| commands.push "show", "Shows a service's configuration and state." do |context, args|
if args.size < 1 if args.size < 1
STDERR << "usage: service show <id> [id [...]]\n" STDERR << "usage: service show <id> [id [...]]\n"
next next
@ -267,25 +277,25 @@ commands.push "show", "Shows a service's configuration and state." do |args|
end end
end end
commands.push "add-environment", "Creates a new (empty) environment." do |arg| commands.push "add-environment", "Creates a new (empty) environment." do |context, args|
if arg.size != 1 if args.size != 1
STDERR.puts "usage: service add-environment <name>" STDERR.puts "usage: service add-environment <name>"
exit 1 exit 1
end end
Environment.new(arg[0]).write ENVIRONMENTS_DIRECTORY Environment.new(args[0]).write ENVIRONMENTS_DIRECTORY
end end
commands.push "list-environments", "Lists all currently defined environments.s", do |arg| commands.push "list-environments", "Lists all currently defined environments.s", do |context, args|
Environment.all.map do |env| Environment.all.map do |env|
puts env.to_s puts env.to_s
end end
end end
commands.push "del-environment", "Removes an empty environment." do |arg| commands.push "del-environment", "Removes an empty environment." do |context, args|
rvalue = 0 rvalue = 0
arg.each do |arg| args.each do |arg|
environment = Environment.all.find &.name.==(arg) environment = Environment.all.find &.name.==(arg)
if environment.nil? if environment.nil?
@ -346,11 +356,13 @@ ServiceDefinition.load SERVICES_DIRECTORY
Environment.load ENVIRONMENTS_DIRECTORY Environment.load ENVIRONMENTS_DIRECTORY
Service.load RC_DIRECTORY Service.load RC_DIRECTORY
context = Service::Context.new
begin begin
args.shift args.shift
command.[2].call(args) command.[2].call(context, args)
rescue e : Service::Exception rescue e : Service::Exception
STDERR << e.message << "\n" context.error e.message
exit 2 exit 2
end end

View File

@ -227,6 +227,9 @@ class Service
end end
# FIXME: Is working on ${} really a good idea? # FIXME: Is working on ${} really a good idea?
# FIXME: There should be a unified way of obtaining a services
# environment, and we should take our variables from that
# instead of hardcoding everything.
private def evaluate(string) private def evaluate(string)
string.gsub /\${[a-zA-Z_]+}/ do |match| string.gsub /\${[a-zA-Z_]+}/ do |match|
match = match[2..match.size-2] match = match[2..match.size-2]
@ -235,6 +238,8 @@ class Service
@environment.name @environment.name
elsif match.downcase == "service_root" elsif match.downcase == "service_root"
root root
elsif match.downcase == "service_name"
name
else else
"" ""
end end
@ -245,7 +250,15 @@ class Service
@environment.files + @reference.files @environment.files + @reference.files
end end
def start(pid_dir : String, log_dir : String) def start(context : Context)
return if running? context.pid_directory
if non_runnable
context.title "Setting up #{to_s}"
else
context.title "Starting #{to_s}"
end
FileUtils.mkdir_p root FileUtils.mkdir_p root
files.each do |file| files.each do |file|
@ -262,7 +275,7 @@ class Service
next unless run_hook next unless run_hook
puts " - Creating #{file.name}" context.info "Creating #{file.name}"
child = Process.fork do child = Process.fork do
Dir.cd root Dir.cd root
@ -279,12 +292,14 @@ class Service
end end
end end
return if non_runnable
# FIXME: Should evaluate be used in split_command? What namespace should split_command use? # FIXME: Should evaluate be used in split_command? What namespace should split_command use?
command, args = split_command command command, args = split_command command
args.map! do |arg| evaluate arg end args.map! do |arg| evaluate arg end
process = Process.fork do process = Process.fork do
base_log_name = "#{log_dir}/#{name}.#{@environment.name}" base_log_name = "#{context.log_directory}/#{name}.#{@environment.name}"
stdout_file = File.open "#{base_log_name}.out", "w" stdout_file = File.open "#{base_log_name}.out", "w"
stderr_file = File.open "#{base_log_name}.err", "w" stderr_file = File.open "#{base_log_name}.err", "w"
@ -303,20 +318,19 @@ class Service
env: build_environment env: build_environment
end end
self.save_pid pid_dir, process.pid self.save_pid context.pid_directory, process.pid
end end
# TODO: # TODO:
# - Custom shutdown commands. # - Custom shutdown commands.
# - Should we wait for the process to die? # - Should we wait for the process to die?
# - Shouldnt we remove the pid file? # - Shouldnt we remove the pid file?
def stop(pid_dir : String) def stop(context : Context)
if non_runnable return if non_runnable
STDOUT << ("%-20s " % "#{full_id}:").colorize(:white).to_s
STDOUT << "non runnable" context.title "Stopping #{to_s}"
return
end _pid = pid context.pid_directory
_pid = pid pid_dir
if _pid if _pid
command = stop_command command = stop_command
@ -329,7 +343,7 @@ class Service
end end
Process.waitpid _pid Process.waitpid _pid
File.delete(get_pid_file pid_dir) File.delete(get_pid_file context.pid_directory)
else else
# Already stopped or dead, nothing to be done here. # Already stopped or dead, nothing to be done here.
end end
@ -457,15 +471,19 @@ class Service
File.write "#{path}/#{name}.#{@environment.name}.spec", to_spec File.write "#{path}/#{name}.#{@environment.name}.spec", to_spec
end end
def remove(path) def remove(context)
context.title "Removing #{to_s}"
files.reverse.each do |file| files.reverse.each do |file|
puts " - removing '#{file.file_path}'" file_path = evaluate file.file_path
context.info "Removing '#{file_path}'"
command = file.deletion_command command = file.deletion_command
child = Process.fork do child = Process.fork do
Dir.cd root Dir.cd root
exit 0 unless File.exists? file.file_path exit 0 unless File.exists? file_path
if command if command
Process.exec "sh", ["-c", command], Process.exec "sh", ["-c", command],
@ -473,7 +491,7 @@ class Service
error: Process::Redirect::Inherit, error: Process::Redirect::Inherit,
env: build_environment env: build_environment
else else
FileUtils.rm_rf file.file_path FileUtils.rm_rf file_path
end end
end.wait end.wait
@ -483,7 +501,7 @@ class Service
end end
end end
File.delete "#{path}/#{name}.#{@environment.name}.spec" File.delete "#{context.services_directory}/#{name}.#{@environment.name}.spec"
end end
def self.get_by_id(id) def self.get_by_id(id)