diff --git a/project.zsh b/project.zsh index 19861a5..7aa3e13 100644 --- a/project.zsh +++ b/project.zsh @@ -8,9 +8,9 @@ variables+=( VARSTATEDIR '/var' ) -targets=(service status get-port) +targets=(service status get-port gen-config) -for target in service status get-port; do +for target in service status get-port gen-config; do type[$target]=crystal sources[$target]=src/${target}.cr depends[$target]=src/config.cr diff --git a/shard.yml b/shard.yml index d81b2c6..8053bc9 100644 --- a/shard.yml +++ b/shard.yml @@ -15,6 +15,8 @@ targets: dependencies: specparser: - git: https://git.karchnu.fr/WeirdOS/recipes-parser + git: https://git.karchnu.fr/WeirdOS/recipes-parser + crinja: + github: straight-shoota/crinja license: MIT diff --git a/src/gen-config.cr b/src/gen-config.cr new file mode 100644 index 0000000..d4498c0 --- /dev/null +++ b/src/gen-config.cr @@ -0,0 +1,106 @@ +require "crinja" + +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" + + # FIXME: Alter default sources at build-time. + # FIXME: We’ll want a way to alter those context-wide. + sources = [ + "/etc/templates", + "/usr/share/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 + + 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 +end +