Merge branch 'master' of ssh://git.karchnu.fr:2202/WeirdOS/service
commit
d43eddf726
|
@ -7,7 +7,7 @@ def sanitize_path(path)
|
||||||
path.gsub /\/\/+/, "/"
|
path.gsub /\/\/+/, "/"
|
||||||
end
|
end
|
||||||
|
|
||||||
module Configure
|
module GenConfig
|
||||||
def self.parse_options(unparsed : Array(String))
|
def self.parse_options(unparsed : Array(String))
|
||||||
options = ARGV.map(&.split '=')
|
options = ARGV.map(&.split '=')
|
||||||
|
|
||||||
|
@ -32,10 +32,10 @@ module Configure
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Configure::Exception < Exception
|
class GenConfig::Exception < Exception
|
||||||
end
|
end
|
||||||
|
|
||||||
class Configure::Context
|
class GenConfig::Context
|
||||||
getter root : String
|
getter root : String
|
||||||
|
|
||||||
def initialize(@root)
|
def initialize(@root)
|
||||||
|
@ -116,11 +116,11 @@ end
|
||||||
ARGV.shift
|
ARGV.shift
|
||||||
ARGV.shift
|
ARGV.shift
|
||||||
|
|
||||||
options = Configure.parse_options ARGV
|
options = GenConfig.parse_options ARGV
|
||||||
|
|
||||||
begin
|
begin
|
||||||
Configure::Context.new("/").generate(template, target, options)
|
GenConfig::Context.new("/").generate(template, target, options)
|
||||||
rescue e : Configure::Exception
|
rescue e : GenConfig::Exception
|
||||||
STDERR.puts "Fatal error: #{e.message}"
|
STDERR.puts "Fatal error: #{e.message}"
|
||||||
exit 1
|
exit 1
|
||||||
rescue e : Crinja::TypeError
|
rescue e : Crinja::TypeError
|
||||||
|
|
|
@ -1,11 +1,33 @@
|
||||||
require "file_utils"
|
require "file_utils"
|
||||||
|
require "option_parser"
|
||||||
|
|
||||||
require "./config.cr"
|
require "./config.cr"
|
||||||
|
|
||||||
START_PORT = 49152
|
START_PORT = 49152
|
||||||
PORTS_CACHE_DIRECTORY = "#{CACHE_DIRECTORY}/ports/"
|
PORTS_CACHE_DIRECTORY = "#{CACHE_DIRECTORY}/ports/"
|
||||||
|
|
||||||
service = ARGV[0]
|
service = ""
|
||||||
wanted_default_port = ARGV[1]?.try &.to_i
|
wanted_default_port : String? = nil
|
||||||
|
|
||||||
|
parser = OptionParser.parse do |parser|
|
||||||
|
parser.banner = "usage: get-post <id> [default-port] [options]\n" +
|
||||||
|
"options:\n"
|
||||||
|
|
||||||
|
parser.on "-h", "--help", "Prints this help message." do
|
||||||
|
puts parser
|
||||||
|
exit 0
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.unknown_args do |arg|
|
||||||
|
if arg.size < 1 || arg.size > 2
|
||||||
|
puts parser
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
service = arg[0]
|
||||||
|
wanted_default_port = arg[1]?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
service_port_file = "#{PORTS_CACHE_DIRECTORY}/#{service.gsub /\//, ":"}"
|
service_port_file = "#{PORTS_CACHE_DIRECTORY}/#{service.gsub /\//, ":"}"
|
||||||
|
|
||||||
|
|
321
src/service.cr
321
src/service.cr
|
@ -13,17 +13,6 @@ args = [] of String
|
||||||
|
|
||||||
parser = OptionParser.parse do |parser|
|
parser = OptionParser.parse do |parser|
|
||||||
parser.banner = "usage: service <command> [options]\n" +
|
parser.banner = "usage: service <command> [options]\n" +
|
||||||
"\n" +
|
|
||||||
"commands:\n" +
|
|
||||||
" start Starts a stopped or dead service.\n" +
|
|
||||||
" stop Stops a running service.\n" +
|
|
||||||
" status Shows the current state of a service.\n" +
|
|
||||||
" show Describe a service in detail.\n" +
|
|
||||||
" add Add a service to an environment.\n" +
|
|
||||||
" del Remove a service from an environment.\n" +
|
|
||||||
" list Lists registered services.\n" +
|
|
||||||
" list-environments Lists registered environments.\n" +
|
|
||||||
"\n" +
|
|
||||||
"options:\n"
|
"options:\n"
|
||||||
|
|
||||||
parser.on "-h", "--help", "Prints this help message." do
|
parser.on "-h", "--help", "Prints this help message." do
|
||||||
|
@ -35,9 +24,186 @@ parser = OptionParser.parse do |parser|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
command = args[0]?
|
alias Command = Proc(Array(String), Nil)
|
||||||
|
alias CommandTuple = Tuple(String, String, Command)
|
||||||
|
class CommandsList
|
||||||
|
def initialize
|
||||||
|
@commands = Array(CommandTuple).new
|
||||||
|
end
|
||||||
|
|
||||||
|
def push(name : String, description : String, &proc : Command)
|
||||||
|
@commands << Tuple.new(name, description, proc)
|
||||||
|
end
|
||||||
|
|
||||||
|
def find(&block : Proc(CommandTuple, Bool))
|
||||||
|
@commands.find do |tuple|
|
||||||
|
if block.call tuple
|
||||||
|
next true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"commands:\n\n"+ @commands.map do |tuple|
|
||||||
|
" %-32s %s" % [tuple[0], tuple[1]]
|
||||||
|
end.join "\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
commands = CommandsList.new
|
||||||
|
|
||||||
|
commands.push "add", "Adds a service to an environment." do |args|
|
||||||
|
providers = Hash(String, String).new
|
||||||
|
|
||||||
|
environment, service = Service.parse_id args[0]
|
||||||
|
|
||||||
|
args.each_with_index do |arg, i|
|
||||||
|
next if i == 0
|
||||||
|
|
||||||
|
match = arg.match /(.*)=(.*)/
|
||||||
|
|
||||||
|
if match.nil?
|
||||||
|
raise ::Service::Exception.new "usage: service add <service> <token=provider>"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
providers[match[1]] = match[2]
|
||||||
|
end
|
||||||
|
|
||||||
|
Service.new(service, environment).tap do |service|
|
||||||
|
service.consumes.each do |token|
|
||||||
|
provider = providers[token.token]?
|
||||||
|
|
||||||
|
if provider.nil?
|
||||||
|
provider = service.get_default_provider token.token
|
||||||
|
end
|
||||||
|
|
||||||
|
if provider.nil?
|
||||||
|
STDERR.puts "This service consumes a “#{token.token}” token, but you have not specified what other service is supposed to provide it."
|
||||||
|
STDERR.puts "Use the `service add #{args[1]} #{token.token}=<provider>` syntax to specify it."
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
service.providers[token.token] = provider
|
||||||
|
end
|
||||||
|
pp! service.providers
|
||||||
|
end.write RC_DIRECTORY
|
||||||
|
end
|
||||||
|
|
||||||
|
commands.push "del", "Removes a service from an environment." do |args|
|
||||||
|
if args.size < 1
|
||||||
|
STDERR.puts "usage: service del <id> [id [...]]"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
args.each do |id|
|
||||||
|
Service.get_by_id(id).try &.remove RC_DIRECTORY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
commands.push "start", "Starts a service." do
|
||||||
|
services = args.map do |arg|
|
||||||
|
service = Service.get_by_id(arg)
|
||||||
|
|
||||||
|
unless service
|
||||||
|
raise Service::Exception.new "Service '#{arg}' does not exist."
|
||||||
|
end
|
||||||
|
|
||||||
|
service
|
||||||
|
end
|
||||||
|
|
||||||
|
services.each do |service|
|
||||||
|
service.dependency_tree.flatten.reverse.each do |service|
|
||||||
|
next if service.running? PID_DIRECTORY
|
||||||
|
|
||||||
|
puts "starting #{service.to_s}"
|
||||||
|
service.start PID_DIRECTORY, LOG_DIRECTORY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
commands.push "stop", "Stops a running service." do |args|
|
||||||
|
services = args.map do |arg|
|
||||||
|
service = Service.get_by_id(arg)
|
||||||
|
|
||||||
|
unless service
|
||||||
|
raise Service::Exception.new "Service '#{arg}' does not exist."
|
||||||
|
end
|
||||||
|
|
||||||
|
service
|
||||||
|
end
|
||||||
|
|
||||||
|
services.each do |service|
|
||||||
|
# FIXME: Build revdep tree and stop services started as dependencies?
|
||||||
|
next if ! service.running? PID_DIRECTORY
|
||||||
|
# FIXME: Should we remove duplicate services from the
|
||||||
|
# tree once flattened?
|
||||||
|
service.reverse_dependency_tree.flatten.reverse.each do |service|
|
||||||
|
next if ! service.running? PID_DIRECTORY
|
||||||
|
|
||||||
|
puts "stopping #{service.to_s}"
|
||||||
|
service.stop PID_DIRECTORY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
commands.push "status", "Prints the status of services." do |args|
|
||||||
|
child = Process.run "#{OWN_LIBEXEC_DIR}/status", args,
|
||||||
|
output: Process::Redirect::Inherit,
|
||||||
|
error: Process::Redirect::Inherit
|
||||||
|
return_value = (child.exit_status / 256).to_i
|
||||||
|
|
||||||
|
# Errors not registered here should probably be verbose in `status`.
|
||||||
|
if return_value == 1
|
||||||
|
STDERR << "No such service.\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
exit return_value
|
||||||
|
end
|
||||||
|
|
||||||
|
commands.push "show", "Shows a service's configuration and state." do |args|
|
||||||
|
if args.size < 1
|
||||||
|
STDERR << "usage: service show <id> [id [...]]\n"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
args.each do |arg|
|
||||||
|
environment_name, service_name = Service.parse_id arg
|
||||||
|
|
||||||
|
service = Service.all.find do |service|
|
||||||
|
service.name == service_name &&
|
||||||
|
service.environment.name == environment_name
|
||||||
|
end
|
||||||
|
|
||||||
|
if service
|
||||||
|
puts service.summary
|
||||||
|
else
|
||||||
|
STDERR << "No such service is registered.\n"
|
||||||
|
exit 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
commands.push "add-environment", "Creates a new (empty) environment." do |arg|
|
||||||
|
Environment.new(args[9]).write ENVIRONMENTS_DIRECTORY
|
||||||
|
end
|
||||||
|
|
||||||
|
commands.push "list-environments", "Lists all currently defined environments.s", do |arg|
|
||||||
|
Environment.all.map do |env|
|
||||||
|
puts env.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
commands.push "help", "Prints this help message." do
|
||||||
|
puts parser
|
||||||
|
puts
|
||||||
|
puts commands.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
command = commands.find &.[0].==(args[0]?)
|
||||||
if command.nil?
|
if command.nil?
|
||||||
STDERR << parser << "\n"
|
STDERR << parser << "\n"
|
||||||
|
STDERR << "\n"
|
||||||
|
STDERR << commands.to_s << "\n"
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -46,135 +212,8 @@ Environment.load ENVIRONMENTS_DIRECTORY
|
||||||
Service.load RC_DIRECTORY
|
Service.load RC_DIRECTORY
|
||||||
|
|
||||||
begin
|
begin
|
||||||
if args[0] == "help"
|
args.shift
|
||||||
puts parser
|
command.[2].call(args)
|
||||||
elsif args[0] == "add"
|
|
||||||
providers = Hash(String, String).new
|
|
||||||
|
|
||||||
environment, service = Service.parse_id args[1]
|
|
||||||
|
|
||||||
args.shift
|
|
||||||
args.each_with_index do |arg, i|
|
|
||||||
next if i == 0
|
|
||||||
|
|
||||||
match = arg.match /(.*)=(.*)/
|
|
||||||
|
|
||||||
if match.nil?
|
|
||||||
raise ::Service::Exception.new "usage: service add <service> <token=provider>"
|
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
providers[match[1]] = match[2]
|
|
||||||
end
|
|
||||||
|
|
||||||
Service.new(service, environment).tap do |service|
|
|
||||||
service.consumes.each do |token|
|
|
||||||
provider = providers[token.token]?
|
|
||||||
|
|
||||||
if provider.nil?
|
|
||||||
provider = service.get_default_provider token.token
|
|
||||||
end
|
|
||||||
|
|
||||||
if provider.nil?
|
|
||||||
STDERR.puts "This service consumes a “#{token.token}” token, but you have not specified what other service is supposed to provide it."
|
|
||||||
STDERR.puts "Use the `service add #{args[1]} #{token.token}=<provider>` syntax to specify it."
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
service.providers[token.token] = provider
|
|
||||||
end
|
|
||||||
pp! service.providers
|
|
||||||
end.write RC_DIRECTORY
|
|
||||||
elsif args[0] == "del"
|
|
||||||
Service.new(args[1], args[2]?).remove RC_DIRECTORY
|
|
||||||
elsif args[0] == "start"
|
|
||||||
services = args[1..args.size].map do |arg|
|
|
||||||
service = Service.get_by_id(arg)
|
|
||||||
|
|
||||||
unless service
|
|
||||||
raise Service::Exception.new "Service '#{arg}' does not exist."
|
|
||||||
end
|
|
||||||
|
|
||||||
service
|
|
||||||
end
|
|
||||||
|
|
||||||
services.each do |service|
|
|
||||||
service.dependency_tree.flatten.reverse.each do |service|
|
|
||||||
next if service.running? PID_DIRECTORY
|
|
||||||
|
|
||||||
puts "starting #{service.to_s}"
|
|
||||||
service.start PID_DIRECTORY, LOG_DIRECTORY
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elsif args[0] == "stop"
|
|
||||||
services = args[1..args.size].map do |arg|
|
|
||||||
service = Service.get_by_id(arg)
|
|
||||||
|
|
||||||
unless service
|
|
||||||
raise Service::Exception.new "Service '#{arg}' does not exist."
|
|
||||||
end
|
|
||||||
|
|
||||||
service
|
|
||||||
end
|
|
||||||
|
|
||||||
services.each do |service|
|
|
||||||
# FIXME: Build revdep tree and stop services started as dependencies?
|
|
||||||
next if ! service.running? PID_DIRECTORY
|
|
||||||
# FIXME: Should we remove duplicate services from the
|
|
||||||
# tree once flattened?
|
|
||||||
service.reverse_dependency_tree.flatten.reverse.each do |service|
|
|
||||||
next if ! service.running? PID_DIRECTORY
|
|
||||||
|
|
||||||
puts "stopping #{service.to_s}"
|
|
||||||
service.stop PID_DIRECTORY
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elsif args[0] == "status"
|
|
||||||
args.shift
|
|
||||||
child = Process.run "#{OWN_LIBEXEC_DIR}/status", args,
|
|
||||||
output: Process::Redirect::Inherit,
|
|
||||||
error: Process::Redirect::Inherit
|
|
||||||
return_value = (child.exit_status / 256).to_i
|
|
||||||
|
|
||||||
# Errors not registered here should probably be verbose in `status`.
|
|
||||||
if return_value == 1
|
|
||||||
STDERR << "No such service.\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
exit return_value
|
|
||||||
elsif args[0] == "show"
|
|
||||||
service = Service.all.find do |service|
|
|
||||||
unless service.name == args[1]
|
|
||||||
next false
|
|
||||||
end
|
|
||||||
|
|
||||||
env = args[2]? || "root"
|
|
||||||
if service.environment.name != env
|
|
||||||
next false
|
|
||||||
end
|
|
||||||
|
|
||||||
true
|
|
||||||
end
|
|
||||||
if service
|
|
||||||
puts service.summary
|
|
||||||
else
|
|
||||||
STDERR << "No such service is registered.\n"
|
|
||||||
exit 2
|
|
||||||
end
|
|
||||||
elsif args[0] == "list"
|
|
||||||
Service.all.map do |service|
|
|
||||||
puts service.to_s
|
|
||||||
end
|
|
||||||
elsif args[0] == "add-environment"
|
|
||||||
Environment.new(args[1]).write ENVIRONMENTS_DIRECTORY
|
|
||||||
elsif args[0] == "list-environments"
|
|
||||||
Environment.all.map do |env|
|
|
||||||
puts env.to_s
|
|
||||||
end
|
|
||||||
else
|
|
||||||
STDERR << parser << "\n"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
rescue e : Service::Exception
|
rescue e : Service::Exception
|
||||||
STDERR << e.message << "\n"
|
STDERR << e.message << "\n"
|
||||||
exit 2
|
exit 2
|
||||||
|
|
|
@ -26,10 +26,9 @@ class Environment
|
||||||
directory: "/srv/${ENVIRONMENT}"
|
directory: "/srv/${ENVIRONMENT}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(specs : SpecParser)
|
def initialize(@name, specs : SpecParser)
|
||||||
assignments = specs.assignments
|
assignments = specs.assignments
|
||||||
|
|
||||||
@name = assignments["name"].as_s
|
|
||||||
assignments["type"].try &.as_s.tap do |type|
|
assignments["type"].try &.as_s.tap do |type|
|
||||||
@type = Type.parse type
|
@type = Type.parse type
|
||||||
end
|
end
|
||||||
|
@ -53,7 +52,10 @@ class Environment
|
||||||
file_path = "#{path}/#{child}"
|
file_path = "#{path}/#{child}"
|
||||||
|
|
||||||
begin
|
begin
|
||||||
environment = Environment.new SpecParser.parse(File.read file_path).not_nil!
|
name = File.basename(child, ".spec")
|
||||||
|
specs = SpecParser.parse File.read(file_path)
|
||||||
|
|
||||||
|
environment = Environment.new name, specs
|
||||||
rescue e
|
rescue e
|
||||||
STDERR << "error loading #{file_path}: " << e << "\n"
|
STDERR << "error loading #{file_path}: " << e << "\n"
|
||||||
# FIXME: Print stacktrace? Debug mode?
|
# FIXME: Print stacktrace? Debug mode?
|
||||||
|
@ -70,7 +72,6 @@ class Environment
|
||||||
|
|
||||||
def to_spec
|
def to_spec
|
||||||
[
|
[
|
||||||
"name: #{@name}",
|
|
||||||
"type: #{@type.to_s}"
|
"type: #{@type.to_s}"
|
||||||
].join("\n") + "\n"
|
].join("\n") + "\n"
|
||||||
end
|
end
|
||||||
|
|
|
@ -47,10 +47,9 @@ class ServiceDefinition
|
||||||
getter checks : Array(Checks)
|
getter checks : Array(Checks)
|
||||||
getter provides : Array(Provides)
|
getter provides : Array(Provides)
|
||||||
|
|
||||||
def initialize(specs : SpecParser)
|
def initialize(@name, specs : SpecParser)
|
||||||
sections = specs.sections
|
sections = specs.sections
|
||||||
specs = specs.assignments
|
specs = specs.assignments
|
||||||
@name = specs["name"].as_s
|
|
||||||
@command = specs["command"].as_s
|
@command = specs["command"].as_s
|
||||||
@stop_command = specs["stop-command"]?.try &.as_s
|
@stop_command = specs["stop-command"]?.try &.as_s
|
||||||
@directory = specs["directory"]?.try &.as_s
|
@directory = specs["directory"]?.try &.as_s
|
||||||
|
@ -64,7 +63,10 @@ class ServiceDefinition
|
||||||
def self.load(path)
|
def self.load(path)
|
||||||
Dir.each_child path do |child|
|
Dir.each_child path do |child|
|
||||||
if child.match /\.spec$/
|
if child.match /\.spec$/
|
||||||
@@all << ServiceDefinition.new SpecParser.parse(File.read "#{path}/#{child}").not_nil!
|
name = File.basename(child, ".spec")
|
||||||
|
specs = SpecParser.parse File.read "#{path}/#{child}"
|
||||||
|
|
||||||
|
@@all << ServiceDefinition.new name, specs
|
||||||
else
|
else
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
|
@ -35,7 +35,7 @@ else
|
||||||
if service.nil?
|
if service.nil?
|
||||||
service_not_found = true
|
service_not_found = true
|
||||||
else
|
else
|
||||||
puts "#{service.id}: #{service.status PID_DIRECTORY}"
|
puts "#{service.full_id}: #{service.status PID_DIRECTORY}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue