Reorg daemon code a bit
This commit is contained in:
71
src/daemon/main.cr
Normal file
71
src/daemon/main.cr
Normal file
@ -0,0 +1,71 @@
|
||||
require "./secrets.cr"
|
||||
require "./proxyconf.cr"
|
||||
require "compress/gzip"
|
||||
require "crystar"
|
||||
require "docr"
|
||||
require "kemal-basic-auth"
|
||||
require "kemal"
|
||||
require "uuid"
|
||||
|
||||
# FIXME: make configurable
|
||||
basic_auth "admin", "admin"
|
||||
|
||||
# Bring up the funko
|
||||
get "/funko/:name/up/" do |env|
|
||||
name = env.params.url["name"]
|
||||
response = run_faaso(["up", name])
|
||||
|
||||
if response["exit_code"] != 0
|
||||
halt env, status_code: 500, response: response.to_json
|
||||
else
|
||||
response.to_json
|
||||
end
|
||||
end
|
||||
|
||||
# Build image for funko received as "funko.tgz"
|
||||
# TODO: This may take a while, consider using something like
|
||||
# mosquito-cr/mosquito to make it a job queue
|
||||
post "/funko/build/" do |env|
|
||||
# Create place to build funko
|
||||
tmp_dir = Path.new("tmp", UUID.random.to_s)
|
||||
Dir.mkdir_p(tmp_dir) unless File.exists? tmp_dir
|
||||
|
||||
# Expand tarball in there
|
||||
file = env.params.files["funko.tgz"].tempfile
|
||||
Compress::Gzip::Reader.open(file) do |gzip|
|
||||
Crystar::Reader.open(gzip) do |tar|
|
||||
tar.each_entry do |entry|
|
||||
File.open(Path.new(tmp_dir, entry.name), "w") do |dst|
|
||||
IO.copy entry.io, dst
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Build the thing
|
||||
response = run_faaso(["build", tmp_dir.to_s])
|
||||
|
||||
if response["exit_code"] != 0
|
||||
halt env, status_code: 500, response: response.to_json
|
||||
else
|
||||
response.to_json
|
||||
end
|
||||
end
|
||||
|
||||
def run_faaso(args : Array(String))
|
||||
stderr = IO::Memory.new
|
||||
stdout = IO::Memory.new
|
||||
status = Process.run(
|
||||
command: "faaso",
|
||||
args: args + ["-l"], # Always local in the server
|
||||
output: stdout,
|
||||
error: stderr,
|
||||
)
|
||||
{
|
||||
"exit_code" => status.exit_code,
|
||||
"stdout" => stdout.to_s,
|
||||
"stderr" => stderr.to_s,
|
||||
}
|
||||
end
|
||||
|
||||
Kemal.run
|
59
src/daemon/proxyconf.cr
Normal file
59
src/daemon/proxyconf.cr
Normal file
@ -0,0 +1,59 @@
|
||||
require "kemal"
|
||||
|
||||
module Proxy
|
||||
@@current_config = File.read("tinyproxy.conf")
|
||||
|
||||
# Get current proxy config
|
||||
get "/proxy/" do
|
||||
@@current_config
|
||||
end
|
||||
|
||||
# Bump proxy config to current docker state, returns
|
||||
# new proxy config
|
||||
patch "/proxy/" do
|
||||
Log.info { "Updating routing" }
|
||||
# Get all the funkos, create routes for them all
|
||||
update_proxy_config
|
||||
end
|
||||
|
||||
def self.update_proxy_config
|
||||
docker_api = Docr::API.new(Docr::Client.new)
|
||||
containers = docker_api.containers.list(all: true)
|
||||
|
||||
funkos = [] of String
|
||||
containers.each { |container|
|
||||
names = container.names.select &.starts_with? "/faaso-"
|
||||
next if names.empty?
|
||||
funkos << names[0][7..]
|
||||
}
|
||||
funkos.sort!
|
||||
|
||||
config = %(
|
||||
Port 8888
|
||||
Listen 0.0.0.0
|
||||
Timeout 600
|
||||
Allow 0.0.0.0/0
|
||||
ReverseOnly Yes
|
||||
ReverseMagic Yes
|
||||
ReversePath "/admin/" "http://127.0.0.1:3000/"
|
||||
) + funkos.map { |funko| %(ReversePath "/faaso/#{funko}/" "http://#{funko}:3000/") }.join("\n")
|
||||
|
||||
if @@current_config != config
|
||||
File.open("tinyproxy.conf", "w") do |file|
|
||||
file << config
|
||||
end
|
||||
# Reload config
|
||||
Process.run(command: "/usr/bin/killall", args: ["-USR1", "tinyproxy"])
|
||||
@@current_config = config
|
||||
end
|
||||
config
|
||||
end
|
||||
end
|
||||
|
||||
# Update proxy config once a second
|
||||
spawn do
|
||||
loop do
|
||||
Proxy.update_proxy_config
|
||||
sleep 1.second
|
||||
end
|
||||
end
|
46
src/daemon/secrets.cr
Normal file
46
src/daemon/secrets.cr
Normal file
@ -0,0 +1,46 @@
|
||||
require "kemal"
|
||||
|
||||
module Secrets
|
||||
SECRETS = Hash(String, String).new
|
||||
SECRET_PATH = "./secrets/"
|
||||
|
||||
# TODO: sanitize all inputs
|
||||
|
||||
# Store secrets in a tree of files
|
||||
def self.update_secrets
|
||||
# Save new secrets
|
||||
SECRETS.map do |_name, value|
|
||||
funko, name = _name.split("-", 2)
|
||||
funko_dir = Path.new(SECRET_PATH, funko)
|
||||
Dir.mkdir_p(funko_dir)
|
||||
File.write(Path.new(funko_dir, name), value)
|
||||
end
|
||||
# Delete secrets not in the hash
|
||||
Dir.glob(Path.new(SECRET_PATH, "*")).each do |funko_dir|
|
||||
funko = File.basename(funko_dir)
|
||||
Dir.glob(Path.new(funko_dir, "*")).each do |secret_file|
|
||||
name = File.basename(secret_file)
|
||||
unless SECRETS.has_key?("#{funko}-#{name}")
|
||||
File.delete(secret_file)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Gets a secret in form {"name": "funko_name-secret_name", "value": "secret_value"}
|
||||
post "/secrets/" do |env|
|
||||
name = env.params.json["name"].as(String)
|
||||
value = env.params.json["value"].as(String)
|
||||
SECRETS[name] = value
|
||||
Secrets.update_secrets
|
||||
halt env, status_code: 201, response: "Created"
|
||||
end
|
||||
|
||||
# Deletes a secret from the disk and memory
|
||||
delete "/secrets/:name/" do |env|
|
||||
name = env.params.url["name"]
|
||||
SECRETS.delete(name)
|
||||
update_secrets
|
||||
halt env, status_code: 204, response: "Deleted"
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user