#!/usr/bin/env ruby require 'json' require 'socket' require 'net/http' COLOR_GREEN = "" COLOR_RED = "" COLOR_GRAY = "" COLOR_RESET = "" class Container FIELDS = [:state, :dup, :ouroboros, :name, :vhosts, :authelia, :status, :image, :ports] @@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] vh = @data[:Labels][:"nginx_virtual_host"]&.split(",")&.map{|h| h.split("@").first.strip} 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 end.uniq.join(" ") #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}" elsif @data[:Labels][:"authelia_policy"]=="none" set :authelia, "#{COLOR_GREEN}NONE#{COLOR_RESET}" else set :authelia, "#{COLOR_RED}?#{COLOR_RESET}" end 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