#!/usr/bin/ruby

require 'logger'

class UpdateAllTheThings
  def initialize
    @loglog = Logger.new(STDERR)
    @loglog.level = Logger::INFO

    zypper_update

    ['podman', 'docker'].each do |container_engine|
      if which(container_engine)
        container_update(container_engine)
      else
        @loglog.warn("Container engine #{container_engine} not found")
      end
    end

    zypper_ps_reminder
  end

  private
  #
  # from https://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby
  # Cross-platform way of finding an executable in the $PATH.
  #
  #   which('ruby') #=> /usr/bin/ruby
  def which(cmd)
    exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
    ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
      exts.each do |ext|
        exe = File.join(path, "#{cmd}#{ext}")
        return exe if File.executable?(exe) && !File.directory?(exe)
      end
    end
    nil
  end

  def zypper_update
    @loglog.info("Updating repositories and running dup")

    if system("zypper", "ref") and system("zypper", "dup")
      @loglog.info("Dup successful")
      @loglog.info("Purging kernels")
      system("zypper", "purge-kernels")
      @loglog.info("Restarting pid 1")
      system("systemctl", "daemon-reexec")

      @loglog.info("Getting Services that need restart")
      services_need_restart = []

      IO.popen(["zypper", "ps", "--print", '%s']) do |zypper_ps_fd|
        services_need_restart = zypper_ps_fd.readlines.map { |service| service.chomp }
      end

      unless services_need_restart.empty?
        @loglog.info("Restarting all services: #{services_need_restart}")
        system("systemctl", "restart", *services_need_restart)
      end
    else
      @loglog.warn("refresh or dup failed")
    end
  end

  def zypper_ps_reminder
    @loglog.info("Check if you want to restart/kill any of the following processes:")
    system("zypper", "ps")
  end

  def container_update(container_engine="podman")
    @loglog.info("Updating images for #{container_engine}")
    image_list = []

    @loglog.info("Getting images list for pull")
    IO.popen([container_engine, "image", "ls", "--all", "--filter", "reference=*latest*", "--format", "{{.Repository}}:{{.Tag}}"]) do |image_list_fd|
      image_list = image_list_fd.readlines
    end

    image_list.each do |image_url|
      image_url.chomp!
      @loglog.info("Pulling #{container_engine} => #{image_url}")
      system(container_engine, "pull", image_url)
    end

    image_cleanup_list = []

    @loglog.info("Getting images list for prune")
    IO.popen([container_engine, "image", "ls", "--all", "--format", '{{.Repository}}:{{.Tag}} {{.ID}}']) do |image_list_fd|
      image_list_fd.each do |image_url|
        if image_url =~ /:<none>/
          image_url, image_id = image_url.split(/\s+/)
          image_cleanup_list << image_id
        end
      end
    end

    unless image_cleanup_list.empty?
      @loglog.info("Cleaning up the following image ids: #{container_engine} #{image_list.join(", ")}")
      system(container_engine, "rmi", *image_cleanup_list)
    end

    @loglog.info("Current images list")
    system(container_engine, "image", "ls", "--all")
  end
end

UpdateAllTheThings.new
