Improved UI a bit.

This commit is contained in:
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.
dependencies:
weird-crystal-base:
git: https://git.karchnu.fr/WeirdOS/weird-crystal-base
specparser:
git: https://git.karchnu.fr/WeirdOS/recipes-parser
crinja:

View File

@ -2,10 +2,9 @@ require "option_parser"
require "yaml"
require "colorize"
require "./config.cr"
require "weird-crystal-base"
# TODO:
# - Be more declarative about the definition of commands.
require "./config.cr"
require "./service/*"
@ -16,7 +15,13 @@ args = [] of String
force = 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)
class CommandsList
def initialize
@ -43,7 +48,7 @@ class CommandsList
end
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
domain = nil
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
raise Service::Exception.new "'#{service.name}' requires a domain= parameter to be provided!"
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
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
STDERR.puts "usage: service del <id> [id [...]]"
exit 1
@ -150,7 +171,7 @@ commands.push "del", "Removes a service from an environment." do |args|
service = Service.get_by_id(id)
if service.nil?
STDERR.puts "#{id}: no such service"
context.error "#{id}: no such service"
rvalue = 1
next
end
@ -158,7 +179,7 @@ commands.push "del", "Removes a service from an environment." do |args|
revdeps = service.reverse_dependencies
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
next
end
@ -166,20 +187,18 @@ commands.push "del", "Removes a service from an environment." do |args|
revdeps.reverse.each do |service|
next if ! service.running? PID_DIRECTORY
puts "stopping #{service.to_s}"
service.stop PID_DIRECTORY
service.stop context
end
revdeps.reverse.each do |service|
puts "removing #{service.to_s}"
service.remove RC_DIRECTORY
service.remove context
end
end
exit rvalue
end
commands.push "start", "Starts a service." do
commands.push "start", "Starts a service." do |context, args|
services = args.map do |arg|
service = Service.get_by_id(arg)
@ -192,20 +211,12 @@ commands.push "start", "Starts a service." do
services.each do |service|
service.dependency_tree.flatten.reverse.each do |service|
next if service.running? PID_DIRECTORY
if service.non_runnable
STDOUT << "#{service.to_s}: non runnable\n"
next
end
puts "starting #{service.to_s}"
service.start PID_DIRECTORY, LOG_DIRECTORY
service.start context
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|
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|
next if ! service.running? PID_DIRECTORY
puts "stopping #{service.to_s}"
service.stop PID_DIRECTORY
service.stop context
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
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
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
STDERR << "usage: service show <id> [id [...]]\n"
next
@ -267,25 +277,25 @@ commands.push "show", "Shows a service's configuration and state." do |args|
end
end
commands.push "add-environment", "Creates a new (empty) environment." do |arg|
if arg.size != 1
commands.push "add-environment", "Creates a new (empty) environment." do |context, args|
if args.size != 1
STDERR.puts "usage: service add-environment <name>"
exit 1
end
Environment.new(arg[0]).write ENVIRONMENTS_DIRECTORY
Environment.new(args[0]).write ENVIRONMENTS_DIRECTORY
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|
puts env.to_s
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
arg.each do |arg|
args.each do |arg|
environment = Environment.all.find &.name.==(arg)
if environment.nil?
@ -346,11 +356,13 @@ ServiceDefinition.load SERVICES_DIRECTORY
Environment.load ENVIRONMENTS_DIRECTORY
Service.load RC_DIRECTORY
context = Service::Context.new
begin
args.shift
command.[2].call(args)
command.[2].call(context, args)
rescue e : Service::Exception
STDERR << e.message << "\n"
context.error e.message
exit 2
end

View File

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