diff --git a/src/gen-config.cr b/src/gen-config.cr index 0e46cbb..77e75e5 100644 --- a/src/gen-config.cr +++ b/src/gen-config.cr @@ -7,7 +7,7 @@ def sanitize_path(path) path.gsub /\/\/+/, "/" end -module Configure +module GenConfig def self.parse_options(unparsed : Array(String)) options = ARGV.map(&.split '=') @@ -32,10 +32,10 @@ module Configure end end -class Configure::Exception < Exception +class GenConfig::Exception < Exception end -class Configure::Context +class GenConfig::Context getter root : String def initialize(@root) @@ -116,11 +116,11 @@ end ARGV.shift ARGV.shift -options = Configure.parse_options ARGV +options = GenConfig.parse_options ARGV begin - Configure::Context.new("/").generate(template, target, options) -rescue e : Configure::Exception + GenConfig::Context.new("/").generate(template, target, options) +rescue e : GenConfig::Exception STDERR.puts "Fatal error: #{e.message}" exit 1 rescue e : Crinja::TypeError diff --git a/src/get-port.cr b/src/get-port.cr index 9757484..d1d7b7f 100644 --- a/src/get-port.cr +++ b/src/get-port.cr @@ -1,11 +1,33 @@ require "file_utils" +require "option_parser" + require "./config.cr" START_PORT = 49152 PORTS_CACHE_DIRECTORY = "#{CACHE_DIRECTORY}/ports/" -service = ARGV[0] -wanted_default_port = ARGV[1]?.try &.to_i +service = "" +wanted_default_port : String? = nil + +parser = OptionParser.parse do |parser| + parser.banner = "usage: get-post [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 /\//, ":"}" diff --git a/src/service.cr b/src/service.cr index 0fdb4f7..f5f7150 100644 --- a/src/service.cr +++ b/src/service.cr @@ -13,17 +13,6 @@ args = [] of String parser = OptionParser.parse do |parser| parser.banner = "usage: service [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" parser.on "-h", "--help", "Prints this help message." do @@ -35,9 +24,186 @@ parser = OptionParser.parse do |parser| 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 " + 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}=` 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 [...]]" + 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 [...]]\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? STDERR << parser << "\n" + STDERR << "\n" + STDERR << commands.to_s << "\n" exit 1 end @@ -46,135 +212,8 @@ Environment.load ENVIRONMENTS_DIRECTORY Service.load RC_DIRECTORY begin - if args[0] == "help" - puts parser - 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 " - 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}=` 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 + args.shift + command.[2].call(args) rescue e : Service::Exception STDERR << e.message << "\n" exit 2 diff --git a/src/service/environment.cr b/src/service/environment.cr index 95122e1..5a93f3d 100644 --- a/src/service/environment.cr +++ b/src/service/environment.cr @@ -26,10 +26,9 @@ class Environment directory: "/srv/${ENVIRONMENT}" end - def initialize(specs : SpecParser) + def initialize(@name, specs : SpecParser) assignments = specs.assignments - @name = assignments["name"].as_s assignments["type"].try &.as_s.tap do |type| @type = Type.parse type end @@ -53,7 +52,10 @@ class Environment file_path = "#{path}/#{child}" 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 STDERR << "error loading #{file_path}: " << e << "\n" # FIXME: Print stacktrace? Debug mode? @@ -70,7 +72,6 @@ class Environment def to_spec [ - "name: #{@name}", "type: #{@type.to_s}" ].join("\n") + "\n" end diff --git a/src/service/service_definition.cr b/src/service/service_definition.cr index 7cfdf90..ac76a85 100644 --- a/src/service/service_definition.cr +++ b/src/service/service_definition.cr @@ -47,10 +47,9 @@ class ServiceDefinition getter checks : Array(Checks) getter provides : Array(Provides) - def initialize(specs : SpecParser) + def initialize(@name, specs : SpecParser) sections = specs.sections specs = specs.assignments - @name = specs["name"].as_s @command = specs["command"].as_s @stop_command = specs["stop-command"]?.try &.as_s @directory = specs["directory"]?.try &.as_s @@ -64,7 +63,10 @@ class ServiceDefinition def self.load(path) Dir.each_child path do |child| 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 next end diff --git a/src/status.cr b/src/status.cr index 2e98497..741be59 100644 --- a/src/status.cr +++ b/src/status.cr @@ -35,7 +35,7 @@ else if service.nil? service_not_found = true else - puts "#{service.id}: #{service.status PID_DIRECTORY}" + puts "#{service.full_id}: #{service.status PID_DIRECTORY}" end end