209 lines
4.4 KiB
Crystal
209 lines
4.4 KiB
Crystal
require "crinja"
|
|
|
|
require "passwd"
|
|
|
|
require "./service/service.cr"
|
|
require "./config.cr"
|
|
|
|
def sanitize_path(path)
|
|
path.gsub /\/\/+/, "/"
|
|
end
|
|
|
|
class Service
|
|
alias CrinjaHash = Hash(String, Hash(String, Int32) | String | Crinja::Callable::Instance | Int32 | 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
|
|
|
|
if !non_runnable
|
|
user = Passwd.new("/etc/passwd", "/etc/group")
|
|
.try &.get_user(user_name).not_nil!
|
|
|
|
entry["uid"] = user.uid
|
|
entry["gid"] = user.gid
|
|
entry["user"] = user.login
|
|
end
|
|
|
|
entry["consumers"] = Crinja.function do
|
|
token = arguments.varargs[0].to_s
|
|
|
|
get_consumers(token).map &.id
|
|
end
|
|
|
|
entry["provider"] = Crinja.function do
|
|
token = arguments.varargs[0].to_s
|
|
|
|
providers.find(&.[0].==(token)).try &.[1]
|
|
end
|
|
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))
|
|
context = Service::Context.new
|
|
context.load
|
|
|
|
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 = context.get_service_by_id service_id
|
|
environment = service.environment
|
|
|
|
options["service"] = service.to_genconfig
|
|
|
|
options["providers"] = service.providers.compact_map do |token, provider|
|
|
provider = context.get_service_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["get_service"] = Crinja.function do
|
|
context.get_service_by_id(arguments.varargs[0].to_s).try &.to_genconfig
|
|
end
|
|
|
|
options["raise"] = Crinja.function do
|
|
message = arguments.varargs.join "\n"
|
|
|
|
raise Service::Exception.new "template error: #{message}"
|
|
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 = context.get_service_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
|
|
rescue e
|
|
STDERR.puts "unhandled exception: #{e.message}"
|
|
e.backtrace.map do |line|
|
|
STDERR << " - " << line << '\n'
|
|
end
|
|
end
|
|
|