2024-06-29 17:36:11 +00:00
|
|
|
require "./funko.cr"
|
2024-06-30 18:10:49 +00:00
|
|
|
require "crest"
|
2024-06-28 20:42:10 +00:00
|
|
|
require "docr"
|
|
|
|
require "docr/utils.cr"
|
2024-06-30 18:10:49 +00:00
|
|
|
require "json"
|
2024-06-28 19:24:52 +00:00
|
|
|
require "uuid"
|
2024-06-28 17:09:58 +00:00
|
|
|
|
2024-06-30 18:10:49 +00:00
|
|
|
# API if you just ran faaso-daemon
|
2024-06-30 21:05:02 +00:00
|
|
|
FAASO_SERVER = ENV.fetch("FAASO_SERVER", "http://localhost:3000/")
|
2024-06-30 18:10:49 +00:00
|
|
|
|
2024-06-29 15:29:53 +00:00
|
|
|
# Functions as a Service, Ops!
|
2024-06-28 15:41:21 +00:00
|
|
|
module Faaso
|
|
|
|
VERSION = "0.1.0"
|
|
|
|
|
2024-06-30 00:01:28 +00:00
|
|
|
# Ensure the faaso-net network exists
|
2024-06-29 21:58:49 +00:00
|
|
|
def self.setup_network
|
|
|
|
docker_api = Docr::API.new(Docr::Client.new)
|
|
|
|
docker_api.networks.create(Docr::Types::NetworkConfig.new(
|
|
|
|
name: "faaso-net",
|
|
|
|
check_duplicate: false,
|
|
|
|
driver: "bridge"
|
|
|
|
))
|
2024-06-30 00:01:28 +00:00
|
|
|
rescue ex : Docr::Errors::DockerAPIError
|
|
|
|
raise ex if ex.status_code != 409 # Network already exists
|
|
|
|
|
2024-06-29 21:58:49 +00:00
|
|
|
end
|
|
|
|
|
2024-06-28 17:09:58 +00:00
|
|
|
module Commands
|
2024-06-30 03:55:09 +00:00
|
|
|
# Build images for one or more funkos
|
2024-06-28 17:09:58 +00:00
|
|
|
class Build
|
2024-07-03 14:19:34 +00:00
|
|
|
def run(options, folders : Array(String))
|
|
|
|
funkos = Funko::Funko.from_paths(folders)
|
2024-06-28 17:09:58 +00:00
|
|
|
|
2024-07-03 14:19:34 +00:00
|
|
|
if options["--local"]
|
2024-06-30 18:10:49 +00:00
|
|
|
funkos.each do |funko|
|
|
|
|
# Create temporary build location
|
|
|
|
tmp_dir = Path.new("tmp", UUID.random.to_s)
|
|
|
|
Dir.mkdir_p(tmp_dir) unless File.exists? tmp_dir
|
|
|
|
funko.prepare_build tmp_dir
|
|
|
|
|
2024-06-30 19:57:39 +00:00
|
|
|
Log.info { "Building function... #{funko.name} in #{tmp_dir}" }
|
2024-06-30 18:10:49 +00:00
|
|
|
funko.build tmp_dir
|
|
|
|
end
|
|
|
|
else # Running against a server
|
|
|
|
funkos.each do |funko|
|
|
|
|
# Create a tarball for the funko
|
|
|
|
buf = IO::Memory.new
|
|
|
|
Compress::Gzip::Writer.open(buf) do |gzip|
|
|
|
|
Crystar::Writer.open(gzip) do |tw|
|
|
|
|
Dir.glob("#{funko.path}/**/*").each do |path|
|
|
|
|
next unless File.file? path
|
|
|
|
rel_path = Path[path].relative_to funko.path
|
|
|
|
file_info = File.info(path)
|
|
|
|
hdr = Crystar::Header.new(
|
|
|
|
name: rel_path.to_s,
|
|
|
|
mode: file_info.permissions.to_u32,
|
|
|
|
size: file_info.size,
|
|
|
|
)
|
|
|
|
tw.write_header(hdr)
|
|
|
|
tw.write(File.read(path).to_slice)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
tmp = File.tempname
|
|
|
|
File.open(tmp, "w") do |outf|
|
|
|
|
outf << buf
|
|
|
|
end
|
|
|
|
|
2024-06-30 21:05:02 +00:00
|
|
|
url = "#{FAASO_SERVER}funko/build/"
|
2024-06-30 18:10:49 +00:00
|
|
|
|
|
|
|
begin
|
2024-06-30 21:05:02 +00:00
|
|
|
Log.info { "Uploading funko to #{FAASO_SERVER}" }
|
|
|
|
response = Crest.post(
|
2024-06-30 18:10:49 +00:00
|
|
|
url,
|
|
|
|
{"funko.tgz" => File.open(tmp), "name" => "funko.tgz"},
|
|
|
|
user: "admin", password: "admin"
|
|
|
|
)
|
2024-06-30 19:57:39 +00:00
|
|
|
Log.info { "Build finished successfully." }
|
2024-06-30 21:05:02 +00:00
|
|
|
body = JSON.parse(response.body)
|
|
|
|
Log.info { body["stdout"] }
|
2024-06-30 18:10:49 +00:00
|
|
|
rescue ex : Crest::InternalServerError
|
2024-06-30 19:57:39 +00:00
|
|
|
Log.error { "Error building funko #{funko.name} from #{funko.path}" }
|
2024-06-30 18:10:49 +00:00
|
|
|
body = JSON.parse(ex.response.body)
|
2024-06-30 19:57:39 +00:00
|
|
|
Log.info { body["stdout"] }
|
|
|
|
Log.error { body["stderr"] }
|
2024-06-30 18:10:49 +00:00
|
|
|
exit 1
|
|
|
|
end
|
|
|
|
end
|
2024-06-28 17:09:58 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-06-30 18:38:51 +00:00
|
|
|
# Bring up one or more funkos by name.
|
2024-06-30 03:55:59 +00:00
|
|
|
#
|
2024-06-30 03:41:47 +00:00
|
|
|
# This doesn't guarantee that they will be running the latest
|
|
|
|
# version, and it will try to recicle paused and exited containers.
|
2024-06-30 03:55:59 +00:00
|
|
|
#
|
2024-06-30 03:41:47 +00:00
|
|
|
# If there is no other way, it will create a brand new container with
|
|
|
|
# the latest known image and start it.
|
2024-06-30 03:55:59 +00:00
|
|
|
#
|
2024-06-30 03:41:47 +00:00
|
|
|
# If there are no images for the funko, it will fail to bring it up.
|
2024-06-28 17:09:58 +00:00
|
|
|
class Up
|
|
|
|
@arguments : Array(String) = [] of String
|
2024-07-03 14:19:34 +00:00
|
|
|
@options : Hash(String, Bool)
|
2024-06-28 17:09:58 +00:00
|
|
|
|
|
|
|
def initialize(options, arguments)
|
|
|
|
@options = options
|
|
|
|
@arguments = arguments
|
|
|
|
end
|
|
|
|
|
|
|
|
def run
|
2024-07-02 22:08:14 +00:00
|
|
|
funkos = Funko::Funko.from_names(@arguments)
|
2024-06-29 15:29:53 +00:00
|
|
|
funkos.each do |funko|
|
2024-06-30 18:38:51 +00:00
|
|
|
local = @options.@bool["local"]
|
|
|
|
|
|
|
|
if !local
|
|
|
|
begin
|
2024-06-30 21:05:02 +00:00
|
|
|
response = Crest.get("#{FAASO_SERVER}funko/#{funko.name}/up/",
|
2024-06-30 18:38:51 +00:00
|
|
|
user: "admin", password: "admin")
|
|
|
|
body = JSON.parse(response.body)
|
2024-06-30 19:57:39 +00:00
|
|
|
Log.info { body["stdout"] }
|
2024-06-30 18:38:51 +00:00
|
|
|
next
|
|
|
|
rescue ex : Crest::InternalServerError
|
2024-06-30 19:57:39 +00:00
|
|
|
Log.error { "Error bringing up #{funko.name}" }
|
2024-06-30 18:38:51 +00:00
|
|
|
body = JSON.parse(ex.response.body)
|
2024-06-30 19:57:39 +00:00
|
|
|
Log.info { body["stdout"] }
|
|
|
|
Log.error { body["stderr"] }
|
2024-06-30 18:38:51 +00:00
|
|
|
exit 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-06-30 03:32:31 +00:00
|
|
|
if funko.image_history.empty?
|
2024-06-30 19:57:39 +00:00
|
|
|
Log.error { "Error: no images available for #{funko.name}:latest" }
|
|
|
|
exit 1
|
2024-06-29 17:36:11 +00:00
|
|
|
end
|
2024-06-29 15:29:53 +00:00
|
|
|
|
2024-06-30 03:32:31 +00:00
|
|
|
case funko
|
|
|
|
when .running?
|
|
|
|
# If it's already up, do nothing
|
|
|
|
# FIXME: bring back out-of-date warning
|
2024-06-30 19:57:39 +00:00
|
|
|
Log.info { "#{funko.name} is already up" }
|
2024-06-30 03:32:31 +00:00
|
|
|
when .paused?
|
|
|
|
# If it is paused, unpause it
|
2024-06-30 19:57:39 +00:00
|
|
|
Log.info { "Resuming existing paused container" }
|
2024-06-30 03:32:31 +00:00
|
|
|
funko.unpause
|
|
|
|
when .exited?
|
2024-06-30 19:57:39 +00:00
|
|
|
Log.info { "Starting function #{funko.name}" }
|
|
|
|
Log.info { "Restarting existing exited container" }
|
2024-06-30 03:32:31 +00:00
|
|
|
funko.start
|
|
|
|
else
|
2024-06-30 03:55:09 +00:00
|
|
|
# Only have an image, deploy from scratch
|
2024-06-30 03:32:31 +00:00
|
|
|
Faaso.setup_network # We need it
|
2024-06-30 19:57:39 +00:00
|
|
|
Log.info { "Creating and starting new container" }
|
2024-06-30 03:55:09 +00:00
|
|
|
funko.create_container(autostart: true)
|
2024-06-30 03:32:31 +00:00
|
|
|
|
|
|
|
(1..5).each { |_|
|
2024-06-30 03:36:25 +00:00
|
|
|
break if funko.running?
|
2024-06-30 03:32:31 +00:00
|
|
|
sleep 0.1.seconds
|
|
|
|
}
|
2024-06-30 03:36:25 +00:00
|
|
|
if !funko.running?
|
2024-06-30 19:57:39 +00:00
|
|
|
Log.warn { "Container for #{funko.name} is not running yet" }
|
2024-06-30 03:32:31 +00:00
|
|
|
next
|
|
|
|
end
|
2024-06-30 19:57:39 +00:00
|
|
|
Log.info { "Container for #{funko.name} is running" }
|
2024-06-29 18:19:56 +00:00
|
|
|
end
|
2024-06-28 17:09:58 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-06-30 13:49:50 +00:00
|
|
|
class Export
|
2024-07-03 14:19:34 +00:00
|
|
|
def run(options, source : String, destination : String)
|
|
|
|
funko = Funko::Funko.from_paths([source])[0]
|
|
|
|
# Create temporary build location
|
|
|
|
dst_path = destination
|
|
|
|
if File.exists? dst_path
|
|
|
|
Log.error { "#{dst_path} already exists, not exporting #{funko.path}" }
|
|
|
|
return 1
|
2024-06-29 17:36:11 +00:00
|
|
|
end
|
2024-07-03 14:19:34 +00:00
|
|
|
Log.info { "Exporting #{funko.path} to #{dst_path}" }
|
|
|
|
Dir.mkdir_p(dst_path)
|
|
|
|
funko.prepare_build Path[dst_path]
|
2024-06-29 17:36:11 +00:00
|
|
|
end
|
|
|
|
end
|
2024-06-28 17:09:58 +00:00
|
|
|
end
|
2024-06-28 15:41:21 +00:00
|
|
|
end
|