require "option_parser" require "yaml" require "colorize" require "./config.cr" # TODO: # - Be more declarative about the definition of commands. require "./service/*" 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" + " add-environment Creates a new, empty environment.\n" + " list-environments Lists registered environments.\n" + "\n" + "options:\n" parser.on "-h", "--help", "Prints this help message." do puts parser exit 0 end parser.unknown_args do |x| args = x end end command = args[0]? if command.nil? STDERR << parser << "\n" exit 1 end ServiceDefinition.load SERVICES_DIRECTORY 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] == "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 STDERR << e.message << "\n" exit 2 end