Improved UI a bit.
parent
32dd294c1b
commit
5852a1b818
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 service’s
|
||||
# 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?
|
||||
# - Shouldn’t 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)
|
||||
|
|
Loading…
Reference in New Issue