Switched to docopt because all CLI things suck

This commit is contained in:
Roberto Alsina 2024-07-03 11:19:34 -03:00
parent bef6ded369
commit 11d7cf1f9f
5 changed files with 117 additions and 206 deletions

View File

@ -4,10 +4,6 @@ shards:
git: https://github.com/sija/backtracer.cr.git
version: 1.2.2
commander:
git: https://github.com/mrrooijen/commander.git
version: 0.4.0
crest:
git: https://github.com/mamantoha/crest.git
version: 1.3.13
@ -20,6 +16,10 @@ shards:
git: https://github.com/naqvis/crystar.git
version: 0.4.0
docopt:
git: https://github.com/chenkovsky/docopt.cr.git
version: 0.2.0+git.commit.620fce4f334ff15d7321e5ecb6665ad258fe9297
docr:
git: https://github.com/ralsina/docr.git
version: 0.1.1+git.commit.18f15cc7111b1d0c63347c7cca07aee9ec87a7a8

View File

@ -15,18 +15,18 @@ crystal: ">= 1.12.2"
license: MIT
dependencies:
docr:
github: ralsina/docr
branch: add_exposed_ports
commander:
github: mrrooijen/commander
kemal:
github: kemalcr/kemal
kemal-basic-auth:
github: kemalcr/kemal-basic-auth
crest:
github: mamantoha/crest
crinja:
github: straight-shoota/crinja
crystar:
github: naqvis/crystar
crest:
github: mamantoha/crest
docopt:
github: chenkovsky/docopt.cr
docr:
github: ralsina/docr
branch: add_exposed_ports
kemal:
github: kemalcr/kemal
kemal-basic-auth:
github: kemalcr/kemal-basic-auth

View File

@ -1,5 +1,4 @@
require "./funko.cr"
require "commander"
require "crest"
require "docr"
require "docr/utils.cr"
@ -29,19 +28,10 @@ module Faaso
module Commands
# Build images for one or more funkos
class Build
@arguments : Array(String) = [] of String
@options : Commander::Options
def run(options, folders : Array(String))
funkos = Funko::Funko.from_paths(folders)
def initialize(options, arguments)
@options = options
@arguments = arguments
end
def run
funkos = Funko::Funko.from_paths(@arguments)
local = @options.@bool["local"]
if local
if options["--local"]
funkos.each do |funko|
# Create temporary build location
tmp_dir = Path.new("tmp", UUID.random.to_s)
@ -112,7 +102,7 @@ module Faaso
# If there are no images for the funko, it will fail to bring it up.
class Up
@arguments : Array(String) = [] of String
@options : Commander::Options
@options : Hash(String, Bool)
def initialize(options, arguments)
@options = options
@ -179,64 +169,17 @@ module Faaso
end
class Export
@arguments : Array(String) = [] of String
@options : Commander::Options
def initialize(options, arguments)
@options = options
@arguments = arguments
end
def run
funkos = Funko::Funko.from_paths(@arguments)
funkos.each do |funko|
def run(options, source : String, destination : String)
funko = Funko::Funko.from_paths([source])[0]
# Create temporary build location
dst_path = Path.new("export", funko.name)
dst_path = destination
if File.exists? dst_path
Log.error { "#{dst_path} already exists, not exporting #{funko.path}" }
next
return 1
end
Log.info { "Exporting #{funko.path} to #{dst_path}" }
Dir.mkdir_p(dst_path)
funko.prepare_build dst_path
end
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|
Log.info { "Stopping funko... #{arg}" }
# TODO: check if funko is running
# TODO: stop funko container
# TODO: delete funko container
# TODO: remove route from reverse proxy
end
end
end
class Deploy
@arguments : Array(String) = [] of String
@options : Commander::Options
def initialize(options, arguments)
@options = options
@arguments = arguments
end
def run
@arguments.each do |arg|
Log.info { "Deploying funko... #{arg}" }
# TODO: Everything
end
funko.prepare_build Path[dst_path]
end
end
end

View File

@ -25,6 +25,10 @@ module Funko
@[YAML::Field(ignore: true)]
property path : String = ""
# Scale: how many instances of this funko should be running
@[YAML::Field(ignore: true)]
property scale = 0
# Healthcheck properties
property healthcheck_options : String = "--interval=1m --timeout=2s --start-period=2s --retries=3"
property healthcheck_command : String = "curl --fail http://localhost:3000/ping || exit 1"
@ -60,37 +64,31 @@ module Funko
}
end
# Create an array of funkos just from names. These are limited in function
# and can't call `prepare_build` or some other functionality
def self.from_names(names : Array(String)) : Array(Funko)
names.map { |name|
Funko.from_yaml("name: #{name}")
# Get the number of running instances of this funko
def scale
docker_api = Docr::API.new(Docr::Client.new)
docker_api.containers.list.count { |container|
container.@name.starts_with? "faaso-#{name}-" && container.@state == "running"
}
end
# Get all the funkos docker knows about.
def self.from_docker : Array(Funko)
# Set the number of running instances of this funko
def scale(new_scale : Int)
docker_api = Docr::API.new(Docr::Client.new)
names = [] of String
current_scale = self.scale
return if current_scale == new_scale
# Get all containers that look like funkos
docker_api.containers.list(
all: true,
).each { |container|
container.@names.each { |name|
names << name.split("-", 2)[1].lstrip("/") if name.starts_with?("/faaso-")
if new_scale > current_scale
(current_scale...new_scale).each { start(create_container) }
else
containers.select { |contatiner| container.@state == "running" }.sort! { |i, j|
i.@created <=> j.@created
}.each { |container|
docker_api.containers.stop(container.@id)
current_scale -= 1
break if current_scale == new_scale
}
}
# Now get all images that look like funkos, since
# we can start them just fine.
docker_api.images.list.each { |image|
next if image.@repo_tags.nil?
image.@repo_tags.as(Array(String)).each { |tag|
names << tag.split("-", 2)[1].split(":", 2)[0] if tag.starts_with?("faaso-")
}
}
from_names(names.to_set.to_a.sort!)
end
end
# Setup the target directory `path` with all the files needed
@ -280,5 +278,38 @@ module Funko
docker_api.containers.start(response.@id) if autostart
response.@id
end
# Create an array of funkos just from names. These are limited in function
# and can't call `prepare_build` or some other functionality
def self.from_names(names : Array(String)) : Array(Funko)
names.map { |name|
Funko.from_yaml("name: #{name}")
}
end
# Get all the funkos docker knows about.
def self.from_docker : Array(Funko)
docker_api = Docr::API.new(Docr::Client.new)
names = [] of String
# Get all containers that look like funkos
docker_api.containers.list(
all: true,
).each { |container|
container.@names.each { |name|
names << name.split("-", 2)[1].lstrip("/") if name.starts_with?("/faaso-")
}
}
# Now get all images that look like funkos, since
# we can start them just fine.
docker_api.images.list.each { |image|
next if image.@repo_tags.nil?
image.@repo_tags.as(Array(String)).each { |tag|
names << tag.split("-", 2)[1].split(":", 2)[0] if tag.starts_with?("faaso-")
}
}
from_names(names.to_set.to_a.sort!)
end
end
end

View File

@ -1,6 +1,21 @@
require "./faaso.cr"
require "colorize"
require "commander"
require "docopt"
doc = <<-DOC
FaaSO CLI tool.
Usage:
faaso build FOLDER ... [-l] [-v=<level>]
faaso scale FUNKO_NAME SCALE [-l] [-v=<level>]
faaso export SOURCE DESTINATION [-v=<level>]
Options:
-h --help Show this screen.
--version Show version.
-l --local Run commands locally instead of against a FaaSO server.
-v=level Control the logging verbosity, 0 to 5 [default: 3]
DOC
# Log formatter for
struct LogFormat < Log::StaticFormatter
@ -17,10 +32,7 @@ struct LogFormat < Log::StaticFormatter
string "[#{Time.local}] #{@entry.severity.label}: #{@entry.message}".colorize(@@colors[@entry.severity.label])
end
def self.setup(quiet : Bool, verbosity)
if quiet
_verbosity = Log::Severity::Fatal
else
def self.setup(verbosity)
_verbosity = [
Log::Severity::Fatal,
Log::Severity::Error,
@ -29,7 +41,6 @@ struct LogFormat < Log::StaticFormatter
Log::Severity::Debug,
Log::Severity::Trace,
][[verbosity, 5].min]
end
Log.setup(
_verbosity,
Log::IOBackend.new(io: STDERR, formatter: LogFormat)
@ -37,86 +48,12 @@ struct LogFormat < Log::StaticFormatter
end
end
cli = Commander::Command.new do |cmd|
cmd.use = "faaso"
cmd.long = "Functions as a Service, Open"
ans = Docopt.docopt(doc, ARGV)
LogFormat.setup(ans["-v"].to_s.to_i)
cmd.flags.add do |flag|
flag.name = "local"
flag.short = "-l"
flag.long = "--local"
flag.description = "Run commands locally instead of against a FaaSO server."
flag.default = false
flag.persistent = true
case ans
when .fetch("build", false)
Faaso::Commands::Build.new.run(ans, ans["FOLDER"].as(Array(String)))
when .fetch("export", false)
Faaso::Commands::Export.new.run(ans, ans["SOURCE"].as(String), ans["DESTINATION"].as(String))
end
cmd.flags.add do |flag|
flag.name = "quiet"
flag.short = "-q"
flag.long = "--quiet"
flag.description = "Don't log anything"
flag.default = false
flag.persistent = true
end
cmd.flags.add do |flag|
flag.name = "verbosity"
flag.short = "-v"
flag.long = "--verbosity"
flag.description = "Control the logging verbosity, 0 to 5 "
flag.default = 3
flag.persistent = true
end
cmd.commands.add do |command|
command.use = "build"
command.short = "Build a funko"
command.long = "Build a funko's Docker image and upload it to registry"
command.run do |options, arguments|
LogFormat.setup(options.@bool["quiet"], options.@int["verbosity"])
Faaso::Commands::Build.new(options, arguments).run
end
end
cmd.commands.add do |command|
command.use = "up"
command.short = "Ensure funkos are running"
command.long = "Start/unpause/create containers for requested funkos and ensure they are up."
command.run do |options, arguments|
LogFormat.setup(options.@bool["quiet"], options.@int["verbosity"])
Faaso::Commands::Up.new(options, arguments).run
end
end
cmd.commands.add do |command|
command.use = "deploy"
command.short = "Deploy latest images"
command.long = "Update containers for all funkos to latest image."
command.run do |options, arguments|
LogFormat.setup(options.@bool["quiet"], options.@int["verbosity"])
Faaso::Commands::Deploy.new(options, arguments).run
end
end
cmd.commands.add do |command|
command.use = "down"
command.short = "Stop a funko"
command.long = "Stop a funko in a container"
command.run do |options, arguments|
LogFormat.setup(options.@bool["quiet"], options.@int["verbosity"])
Faaso::Commands::Down.new(options, arguments).run
end
end
cmd.commands.add do |command|
command.use = "export"
command.short = "Export a funko to a directory"
command.long = "Exports a funko as a self-contained directory."
command.run do |options, arguments|
LogFormat.setup(options.@bool["quiet"], options.@int["verbosity"])
Faaso::Commands::Export.new(options, arguments).run
end
end
end
Commander.run(cli, ARGV)