faaso/src/daemon/funko.cr
2024-07-04 16:54:08 -03:00

165 lines
4.5 KiB
Crystal

require "docr"
require "kemal"
require "../funko.cr"
module Funko
extend self
# Get the funko's status
get "/funkos/:name/status/" do |env|
name = env.params.url["name"]
response = run_faaso(["status", name])
if response["exit_code"] != 0
halt env, status_code: 500, response: response.to_json
else
response.to_json
end
end
# Get the funko's scale
get "/funkos/:name/scale/" do |env|
name = env.params.url["name"]
response = run_faaso(["scale", name])
if response["exit_code"] != 0
halt env, status_code: 500, response: response.to_json
else
response.to_json
end
end
# Set the funko's scale
post "/funkos/:name/scale/" do |env|
name = env.params.url["name"]
scale = env.params.body["scale"].as(String)
response = run_faaso(["scale", name, scale])
if response["exit_code"] != 0
Log.error { response }
halt env, status_code: 500, response: response.to_json
else
Log.info { response }
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 "/funkos/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
# Endpoints for the web frontend
# General status for the front page
get "/funkos/" do |env|
funkos = Funko.from_docker
funkos.sort! { |a, b| a.name <=> b.name }
result = [] of Hash(String, String)
funkos.each do |funko|
result << {
"name" => funko.name,
"scale" => funko.scale.to_s,
"containers" => funko.containers.size.to_s,
"images" => funko.images.size.to_s,
}
end
if env.params.query.fetch("format", "json") == "html"
render "src/views/funkos.ecr" else
result.to_json
end
end
# Stop => scale to 0
get "/funkos/:name/stop" do |env|
name = env.params.url["name"]
funko = Funko.from_names([name])[0]
funko.scale(0)
funko.wait_for(0, 1)
end
# Start => scale to 1
get "/funkos/:name/start" do |env|
name = env.params.url["name"]
funko = Funko.from_names([name])[0]
if funko.scale == 0
funko.scale(1)
funko.wait_for(1, 1)
end
end
# Restart => scale to 0, then 1
get "/funkos/:name/restart" do |env|
name = env.params.url["name"]
funko = Funko.from_names([name])[0]
funko.scale(0)
funko.wait_for(0, 1)
funko.scale(1)
funko.wait_for(1, 1)
end
# Return an iframe that shows the container's logs
get "/funkos/:name/terminal/logs" do |env|
name = env.params.url["name"]
funko = Funko.from_names([name])[0]
# FIXME: Just getting the 1st one for now, it
# may not even be running
container_name = funko.containers.map { |c| c.@names[0] }[0]
Terminal.start_terminal(["docker", "logs", "-f", container_name.to_s])
"<iframe src='terminal/' width='100%' height='100%'></iframe>"
end
# Get an iframe with a shell into the container
get "/funkos/:name/terminal/shell" do |env|
name = env.params.url["name"]
funko = Funko.from_names([name])[0]
# FIXME: Just getting the 1st one for now, it
# may not even be running
container_name = funko.containers.map { |c| c.@names[0] }[0].lstrip("/")
Terminal.start_terminal(["docker", "exec", "-ti", container_name, "/bin/sh"], readonly: false)
"<iframe src='terminal/' width='100%' height='100%'></iframe>"
end
# Helper to run faaso locally and get a response back
def run_faaso(args : Array(String))
Log.info { "Running faaso [#{args.join(", ")}, -l]" }
output = IO::Memory.new
status = Process.run(
command: "faaso",
args: args + ["-l"], # Always local in the server
output: output,
error: output,
)
result = {
"exit_code" => status.exit_code,
"output" => output.to_s,
}
result
end
end