require "crinja" require "./service/service.cr" require "./config.cr" def sanitize_path(path) path.gsub /\/\/+/, "/" end module GenConfig alias Variables = String | Array(String) | Array(Variables) | Hash(String, String) | Hash(String, Variables) | Crinja::Callable::Instance def self.parse_options(unparsed : Array(String)) options = ARGV.map(&.split '=') hash = Hash(String, Variables).new options.each do |entry| key, value = entry old_value = hash[key]? case old_value when nil hash[key] = value when String hash[key] = [old_value, value] when Array(String) old_value << value end end hash end end class GenConfig::Exception < Exception end class GenConfig::Context getter root : String def initialize(@root) end def generate(template, target : String, options : Hash(String, Variables)) ServiceDefinition.load SERVICES_DIRECTORY Service.load RC_DIRECTORY target_file = File.open target, "w" sources = [ "#{SYSTEM_CONFIGURATION_DIRECTORY}/templates", "#{SHARED_DATA_DIRECTORY}/templates" ] sources = sources .map(&.+("/#{template}.j2")) .map { |x| sanitize_path x } source = sources.find do |source| if File.exists? source puts "Generating '#{target}' from '#{source}'" next true end end unless source raise Exception.new "Could not find template to generate file ('#{target}')." end pp ENV["SERVICE_ID"]? if service_id = ENV["SERVICE_ID"]? if service = Service.get_by_id service_id environment = service.environment options["id"] = service.full_id options["name"] = service.name options["environment"] = environment.name providers = Hash(String, Variables).new service.providers.each do |token, provider| provider = Service.get_by_id provider next unless provider providers[token] = Hash(String, String).new.tap do |entry| entry["name"] = provider.name entry["id"] = provider.full_id entry["environment"] = provider.environment.name end end options["providers"] = providers end end options["port"] = Crinja.function do service = (arguments.varargs[0]? || "").to_s.gsub /\//, ':' `get-port #{service}`.chomp end options["random_password"] = Crinja.function do id = (arguments.varargs[0]? || options["id"]).to_s password_id = arguments.varargs[1]? || "main" env, service = Service.parse_id id # FIXME: hardcoded path password_file = "/srv/#{env}/#{service}/password_#{password_id}" if File.exists? password_file File.read password_file else password = `dd if=/dev/urandom bs=64 count=1 | base64 -` password = password.gsub('\n', "") File.write password_file, password password end end providers = {} of String => String ENV["SERVICE_TOKENS"]?.try &.split(':').each do |token| providers[token] = ENV["#{token.upcase}_PROVIDER"]? || "" end options["providers"] = providers template = File.read source target_file << Crinja.render(template, options) target_file.close end end template = ARGV[0]? target = ARGV[1]? if target.nil? puts "usage: configure [option1=value1 [option2=value2 […]]]" exit 1 end ARGV.shift ARGV.shift options = GenConfig.parse_options ARGV begin GenConfig::Context.new("/").generate(template, target, options) rescue e : GenConfig::Exception STDERR.puts "Fatal error: #{e.message}" exit 1 rescue e : Crinja::TypeError STDERR.puts "Error reading template: #{e.message}" exit 1 end