2024-06-28 17:09:58 +00:00
|
|
|
require "commander"
|
2024-06-28 20:42:10 +00:00
|
|
|
require "docr"
|
|
|
|
require "docr/utils.cr"
|
2024-06-28 19:24:52 +00:00
|
|
|
require "file_utils"
|
|
|
|
require "uuid"
|
2024-06-28 17:09:58 +00:00
|
|
|
|
2024-06-28 15:41:21 +00:00
|
|
|
# TODO: Write documentation for `Faaso`
|
|
|
|
module Faaso
|
|
|
|
VERSION = "0.1.0"
|
|
|
|
|
2024-06-28 17:09:58 +00:00
|
|
|
module Commands
|
|
|
|
class Build
|
|
|
|
@arguments : Array(String) = [] of String
|
|
|
|
@options : Commander::Options
|
|
|
|
|
|
|
|
def initialize(options, arguments)
|
|
|
|
@options = options
|
|
|
|
@arguments = arguments
|
|
|
|
end
|
|
|
|
|
|
|
|
def run
|
|
|
|
@arguments.each do |arg|
|
|
|
|
# A function is a folder with stuff in it
|
|
|
|
# TODO: decide template based on file extensions or other metadata
|
2024-06-28 19:24:52 +00:00
|
|
|
template = "templates/crystal"
|
|
|
|
tmp_dir = "tmp/#{UUID.random}"
|
2024-06-28 23:07:27 +00:00
|
|
|
slug = arg.gsub("/", "_").strip("_")
|
2024-06-28 22:15:42 +00:00
|
|
|
repo = "localhost:5000"
|
|
|
|
tag = "#{repo}/#{slug}:latest"
|
|
|
|
puts "Building function... #{arg} in #{tmp_dir}"
|
2024-06-28 19:24:52 +00:00
|
|
|
Dir.mkdir_p("tmp") unless File.exists? "tmp"
|
|
|
|
FileUtils.cp_r(template, tmp_dir)
|
|
|
|
Dir.glob(arg + "/**/*").each do |file|
|
|
|
|
FileUtils.cp(file, tmp_dir)
|
|
|
|
end
|
2024-06-28 20:42:10 +00:00
|
|
|
docker_api = Docr::API.new(Docr::Client.new)
|
2024-06-28 22:15:42 +00:00
|
|
|
docker_api.images.build(context: tmp_dir, tags: [tag, "#{slug}:latest"]) { }
|
|
|
|
puts "Pushing to repo as #{tag}"
|
|
|
|
docker_api.images.tag(repo: repo, name: slug, tag: "latest")
|
2024-06-28 23:07:27 +00:00
|
|
|
# FIXME: pushing is broken because my test registry has no auth
|
|
|
|
# docker_api.images.push(name: slug, tag: "latest", auth: "")
|
2024-06-28 17:09:58 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class Up
|
|
|
|
@arguments : Array(String) = [] of String
|
|
|
|
@options : Commander::Options
|
|
|
|
|
|
|
|
def initialize(options, arguments)
|
|
|
|
@options = options
|
|
|
|
@arguments = arguments
|
|
|
|
end
|
|
|
|
|
|
|
|
def run
|
|
|
|
@arguments.each do |arg|
|
2024-06-28 23:07:27 +00:00
|
|
|
slug = arg.gsub("/", "_").strip("_")
|
|
|
|
repo = "localhost:5000"
|
|
|
|
tag = "#{repo}/#{slug}:latest"
|
|
|
|
docker_api = Docr::API.new(Docr::Client.new)
|
|
|
|
# Pull image from registry
|
|
|
|
docker_api.images.create(image: tag)
|
2024-06-28 17:09:58 +00:00
|
|
|
# TODO: Start a container with the image
|
2024-06-28 23:07:27 +00:00
|
|
|
containers = docker_api.containers.list(all: true)
|
|
|
|
pp! containers
|
|
|
|
# If it's running, do nothing
|
2024-06-28 23:57:58 +00:00
|
|
|
if containers.any? { |c|
|
2024-06-28 23:07:27 +00:00
|
|
|
c.@image == tag && c.@state == "running"
|
2024-06-28 23:57:58 +00:00
|
|
|
}
|
2024-06-28 23:07:27 +00:00
|
|
|
puts "#{arg} is already running"
|
|
|
|
return 0
|
|
|
|
end
|
|
|
|
|
2024-06-28 23:57:04 +00:00
|
|
|
# If it is paused, unpause it
|
|
|
|
paused = containers.select { |c|
|
|
|
|
c.@image == tag && c.@state == "paused"
|
|
|
|
}
|
2024-06-28 23:57:58 +00:00
|
|
|
if paused.size > 0
|
2024-06-28 23:57:04 +00:00
|
|
|
puts "Resuming existing paused container"
|
|
|
|
docker_api.containers.unpause(paused[0].@id)
|
|
|
|
return 0
|
|
|
|
end
|
|
|
|
|
2024-06-28 23:07:27 +00:00
|
|
|
# If it is exited, start it
|
|
|
|
existing = containers.select { |c|
|
|
|
|
c.@image == tag && c.@state == "exited"
|
|
|
|
}
|
|
|
|
|
|
|
|
puts "Starting function #{arg}"
|
2024-06-28 23:57:58 +00:00
|
|
|
if existing.size > 0
|
2024-06-28 23:07:27 +00:00
|
|
|
puts "Restarting existing exited container"
|
|
|
|
docker_api.containers.start(existing[0].@id)
|
2024-06-28 23:57:04 +00:00
|
|
|
return 0
|
2024-06-28 23:07:27 +00:00
|
|
|
end
|
2024-06-28 23:57:04 +00:00
|
|
|
|
|
|
|
# Creating from scratch
|
|
|
|
puts "Creating new container"
|
|
|
|
conf = Docr::Types::CreateContainerConfig.new(
|
|
|
|
image: tag,
|
|
|
|
hostname: "foo",
|
|
|
|
host_config: Docr::Types::HostConfig.new
|
|
|
|
)
|
|
|
|
# FIXME: name should be unique
|
|
|
|
response = docker_api.containers.create(name: "fungus", config: conf)
|
|
|
|
docker_api.containers.start(response.@id)
|
2024-06-28 17:09:58 +00:00
|
|
|
end
|
2024-06-28 23:57:04 +00:00
|
|
|
# TODO: Run test for healthcheck
|
|
|
|
# TODO: Map route in reverse proxy to function
|
|
|
|
# TODO: Return function URL for testing
|
2024-06-28 17:09:58 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class Down
|
|
|
|
@arguments : Array(String) = [] of String
|
|
|
|
@options : Commander::Options
|
|
|
|
|
|
|
|
def initialize(options, arguments)
|
|
|
|
@options = options
|
|
|
|
@arguments = arguments
|
|
|
|
end
|
|
|
|
|
|
|
|
def run
|
|
|
|
@arguments.each do |arg|
|
|
|
|
puts "Stopping function... #{arg}"
|
|
|
|
# TODO: check if function is running
|
|
|
|
# TODO: stop function container
|
|
|
|
# TODO: delete function container
|
|
|
|
# TODO: remove route from reverse proxy
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2024-06-28 15:41:21 +00:00
|
|
|
end
|