service/src/gen-config.cr

177 lines
3.7 KiB
Crystal

require "crinja"
require "./service/service.cr"
require "./config.cr"
def sanitize_path(path)
path.gsub /\/\/+/, "/"
end
class Service
alias CrinjaHash = Hash(String, Hash(String, Int32) | String | Nil)
def to_genconfig
CrinjaHash.new.tap do |entry|
entry["name"] = name
entry["id"] = full_id
entry["environment"] = environment.name
entry["root"] = root
entry["domain"] = domain
entry["ports"] = ports
end
end
end
module GenConfig
# FIXME: The fuck has this become.
alias Variables =
String |
Array(String) | Array(Variables) |
Hash(String, String) |
Service::CrinjaHash |
Array(Service::CrinjaHash) |
Hash(String, Array(Service::CrinjaHash)) |
Hash(String, Service::CrinjaHash) |
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))
Environment.load ENVIRONMENTS_DIRECTORY
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
if service_id = ENV["SERVICE_ID"]?
if service = Service.get_by_id service_id
environment = service.environment
options["service"] = service.to_genconfig
options["providers"] = service.providers.compact_map do |token, provider|
provider = Service.get_by_id provider
next unless provider
{token, provider.to_genconfig}
end.to_h
options["consumers"] = service.provides
.map(&.token)
.map{ |token|
{token, service.get_consumers(token).map &.to_genconfig}
}
.to_h
end
end
#options["port"] = Crinja.function do
# service = (arguments.varargs[0]? || "").to_s.gsub /\//, ':'
# `get-port #{service}`.chomp
#end
# FIXME: Move this to a separate binary?
options["random_password"] = Crinja.function do
id = (arguments.varargs[0]? || options["id"]).to_s
password_id = arguments.varargs[1]? || "main"
_service = Service.get_by_id(id).not_nil!
# FIXME: hardcoded path
password_file = "#{_service.root}/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
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 <template-name> <target> [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