2021-11-02 13:14:18 +00:00
|
|
|
|
#!/usr/bin/env ruby
|
|
|
|
|
require 'json'
|
|
|
|
|
require 'socket'
|
|
|
|
|
require 'net/http'
|
|
|
|
|
|
|
|
|
|
COLOR_GREEN = "[1;32m"
|
|
|
|
|
COLOR_RED = "[1;31m"
|
|
|
|
|
COLOR_GRAY = "[1;30m"
|
|
|
|
|
COLOR_RESET = "[0m"
|
|
|
|
|
|
|
|
|
|
class Container
|
2022-10-27 13:03:52 +00:00
|
|
|
|
FIELDS = [:state, :dup, :ouroboros, :name, :vhosts, :authelia, :status, :image, :ports]
|
2021-11-02 13:14:18 +00:00
|
|
|
|
@@lengths = Hash.new(0)
|
|
|
|
|
|
|
|
|
|
def initialize(data)
|
|
|
|
|
@strings = {}
|
|
|
|
|
@data = data
|
|
|
|
|
|
|
|
|
|
set :state, (@data[:State]=="running" ? COLOR_GREEN : COLOR_RED) + @data[:State].capitalize + COLOR_RESET
|
|
|
|
|
set :dup, @data[:Labels][:"de.fabianonline.dup"]=="true" ? "✓" : ""
|
|
|
|
|
set :ouroboros, @data[:Labels][:"com.centurylinklabs.watchtower.enable"]=="true" ? "✓" : ""
|
|
|
|
|
set :name, @data[:Names].first[1..-1]
|
|
|
|
|
|
2022-10-27 13:04:08 +00:00
|
|
|
|
vh = @data[:Labels][:"nginx_virtual_host"]&.split(",")&.map{|h| h.split("@").first.strip}
|
2021-11-02 13:14:18 +00:00
|
|
|
|
if vh.nil? || vh.count==0
|
|
|
|
|
vh = ""
|
|
|
|
|
elsif vh.count==1
|
|
|
|
|
vh = vh.first
|
|
|
|
|
else
|
|
|
|
|
if FULL
|
|
|
|
|
vh = vh.join(", ")
|
|
|
|
|
else
|
|
|
|
|
vh = vh.first + ",…"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
set :vhosts, vh
|
|
|
|
|
|
|
|
|
|
p = @data[:Ports].sort_by{|p| p[:PrivatePort]}.collect do |p|
|
|
|
|
|
type = p[:Type]=="tcp" ? "" : "/#{p[:Type]}"
|
|
|
|
|
if p[:PrivatePort]==p[:PublicPort]
|
|
|
|
|
"#{COLOR_GREEN}#{p[:PrivatePort]}#{type}"
|
|
|
|
|
elsif p[:PublicPort]==nil
|
|
|
|
|
"#{COLOR_GRAY}#{p[:PrivatePort]}#{type}"
|
|
|
|
|
else
|
|
|
|
|
"#{COLOR_RED}#{p[:PrivatePort]}->#{p[:PublicPort]}#{type}"
|
|
|
|
|
end
|
2021-11-02 14:07:30 +00:00
|
|
|
|
end.uniq.join(" ")
|
2021-11-02 13:14:18 +00:00
|
|
|
|
|
2022-10-27 13:03:52 +00:00
|
|
|
|
#set :nginx_allow, @data[:Labels][:"nginx_allow"]
|
|
|
|
|
if vh==""
|
|
|
|
|
set :authelia, ""
|
|
|
|
|
elsif @data[:Labels][:"authelia_policy"]=="one_factor"
|
|
|
|
|
set :authelia, "1#{COLOR_GRAY}FA#{COLOR_RESET}"
|
|
|
|
|
elsif @data[:Labels][:"authelia_policy"]=="two_factor"
|
|
|
|
|
set :authelia, "2#{COLOR_GRAY}FA#{COLOR_RESET}"
|
|
|
|
|
elsif @data[:Labels][:"authelia_policy"]=="bypass"
|
|
|
|
|
set :authelia, "#{COLOR_GREEN}BYP#{COLOR_RESET}"
|
|
|
|
|
else
|
|
|
|
|
set :authelia, "#{COLOR_RED}?#{COLOR_RESET}"
|
|
|
|
|
end
|
|
|
|
|
|
2021-11-02 13:14:18 +00:00
|
|
|
|
st = @data[:Status]
|
|
|
|
|
if st.end_with?("(healthy)")
|
|
|
|
|
st = COLOR_GREEN + st.gsub("(healthy)", "(v)") + COLOR_RESET
|
|
|
|
|
elsif st.end_with?("(unhealthy)")
|
|
|
|
|
st = COLOR_RED + st.gsub("(unhealthy)", "(x)") + COLOR_RESET
|
|
|
|
|
end
|
|
|
|
|
set :status, st
|
|
|
|
|
im = @data[:Image]
|
|
|
|
|
im = im.split(":")[0] unless FULL
|
|
|
|
|
set :image, im
|
|
|
|
|
set :ports, p
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def set(key, value)
|
|
|
|
|
@data[key] = value
|
|
|
|
|
@@lengths[key] = [@@lengths[key], value.cleaned.length].max if value
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def self.lengths; FIELDS.map{|k| @@lengths[k]}; end
|
|
|
|
|
|
|
|
|
|
def data
|
|
|
|
|
FIELDS.map{|k| @data[k]}
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
class String
|
|
|
|
|
def cleaned
|
|
|
|
|
self.gsub(/\[.+?m/, "")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def fix_length(len)
|
|
|
|
|
l = self.cleaned.length
|
|
|
|
|
shortened = self.length - l
|
|
|
|
|
|
|
|
|
|
if l >= len
|
|
|
|
|
return self.slice(0, len + shortened)
|
|
|
|
|
else
|
|
|
|
|
return self + (" " * (len - l))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def fetch_data
|
|
|
|
|
socket = UNIXSocket.new("/var/run/docker.sock")
|
|
|
|
|
request = "GET /v1.24/containers/json?all=1 HTTP/1.0\r\n\r\n"#Host: localhost\r\n\r\n"
|
|
|
|
|
socket.write(request)
|
|
|
|
|
|
|
|
|
|
response = ""
|
|
|
|
|
|
|
|
|
|
loop do
|
|
|
|
|
break if socket.eof?
|
|
|
|
|
line = socket.gets
|
|
|
|
|
break if line=="\r\n"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
until socket.eof?
|
|
|
|
|
line = socket.gets
|
|
|
|
|
response += line
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return JSON.parse(response, symbolize_names: true)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
FULL = ARGV[0]=="--full"
|
|
|
|
|
|
|
|
|
|
data = fetch_data
|
|
|
|
|
data = data.map{|c| Container.new(c).data}
|
|
|
|
|
|
|
|
|
|
lengths = Container.lengths
|
|
|
|
|
|
|
|
|
|
data.sort_by{|d| d[3]}.each do |row|
|
|
|
|
|
row.each_with_index do |d, index|
|
|
|
|
|
if index==row.length-1
|
|
|
|
|
print d.to_s
|
|
|
|
|
else
|
|
|
|
|
print d.to_s.fix_length(lengths[index]) + " "
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
puts
|
|
|
|
|
end
|