require "crinja" require "./service/service.cr" require "./config.cr" def sanitize_path(path) path.gsub /\/\/+/, "/" end module Configure def self.parse_options(unparsed : Array(String)) options = ARGV.map(&.split '=') hash = Hash(String, String | Array(String) | Crinja::Callable::Instance | Hash(String, String)).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 Configure::Exception < Exception end class Configure::Context getter root : String def initialize(@root) end def generate(template, target : String, options : Hash(String, String | Array(String) | Crinja::Callable::Instance | Hash(String, String))) 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 options["id"] = ENV["SERVICE_ID"]? || "" options["environment"] = ENV["ENVIRONMENT"]? || "" options["port"] = Crinja.function do service = (arguments.varargs[0]? || "").to_s.gsub /\//, ':' `get-port #{service}` 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 = Configure.parse_options ARGV begin Configure::Context.new("/").generate(template, target, options) rescue e : Configure::Exception STDERR.puts "Fatal error: #{e.message}" exit 1 rescue e : Crinja::TypeError STDERR.puts "Error reading template: #{e.message}" exit 1 end