#!/usr/bin/env ruby require 'optparse' require 'fileutils' class Jkctl def initialize @options = { env: 'stg' } @repo_root = File.expand_path('..', __dir__) parse_options end def run command = ARGV.shift case command when "k8s" handle_k8s when "db" handle_db else show_help end end private def parse_options OptionParser.new do |opts| opts.banner = "Usage: jkctl [command] [options]" opts.on("-s", "--stg", "Staging environment (default)") { @options[:env] = "stg" } opts.on("-p", "--prd", "Production environment") { @options[:env] = "prd" } end.parse! end def handle_k8s subcommand = ARGV.shift case subcommand when "sync" scope = ARGV.shift # infra or app sync_k8s(scope) when "status" status_k8s else puts "Usage: jkctl k8s [sync|status] [infra|app]" end end def handle_db subcommand = ARGV.shift case subcommand when "backup" backup_db else puts "Usage: jkctl db backup" end end def sync_k8s(scope) unless %w[infra app].include?(scope) puts "Error: sync requires a scope (infra or app)" exit 1 end set_kubeconfig namespace = scope == 'infra' ? 'jam-cloud-infra' : 'jam-cloud' manifest_dir = File.join(@repo_root, 'video-iac', 'k8s', namespace) puts "šŸš€ Syncing k8s #{scope} for #{@options[:env]}..." # Ensure namespace exists ns_file = File.join(manifest_dir, "namespace.yaml") execute("kubectl apply -f #{ns_file}") # Special handling for external-dns (Kustomize) if scope == 'infra' env_dir = @options[:env] == 'stg' ? 'staging' : 'production' ext_dns_dir = File.join(@repo_root, 'video-iac', 'k8s', 'external-dns', 'overlays', env_dir) execute("kubectl apply -k #{ext_dns_dir}") end # Apply all yaml files in the directory (except namespace which we just did) Dir.glob(File.join(manifest_dir, "*.yaml")).each do |file| next if file.end_index?("namespace.yaml") execute("kubectl apply -f #{file}") end # Rollout status for deployments in this scope deployments = `kubectl get deployments -n #{namespace} -o name`.split("\n") deployments.each do |deploy| puts "Checking status for #{deploy}..." execute("kubectl rollout status #{deploy} -n #{namespace}") end status_k8s(namespace) end def status_k8s(filter_ns = nil) set_kubeconfig namespaces = filter_ns ? [filter_ns] : %w[jam-cloud-infra jam-cloud] puts "\nšŸ“Š K8s Status Summary (#{@options[:env]}):" namespaces.each do |ns| puts "\n--- Namespace: #{ns} ---" # Check if namespace exists first exists = system("kubectl get ns #{ns} > /dev/null 2>&1") unless exists puts "Namespace does not exist yet." next end puts "Pods:" # Get pod status, restarts, and age output = `kubectl get pods -n #{ns} -o custom-columns=NAME:.metadata.name,STATUS:.status.phase,RESTARTS:.status.containerStatuses[0].restartCount,AGE:.metadata.creationTimestamp` puts output.empty? ? "No pods found." : output puts "\nServices:" output = `kubectl get svc -n #{ns} -o wide` puts output.empty? ? "No services found." : output end end def backup_db puts "Backing up DB for #{@options[:env]} (Skipped per instruction)..." end def set_kubeconfig if @options[:env] == 'stg' # Extract path from activate-stg script activate_script = File.join(Dir.home, 'bin', 'activate-stg') if File.exist?(activate_script) content = File.read(activate_script) if content =~ /export KUBECONFIG=(.+)/ path = $1.strip.gsub('~', Dir.home) ENV['KUBECONFIG'] = path end else puts "Warning: #{activate_script} not found. Ensure KUBECONFIG is set manually." end else # Placeholder for production kubeconfig logic puts "Error: Production kubeconfig path not defined." exit 1 end end def execute(cmd) puts "Executing: #{cmd}" system(cmd) || (puts "Command failed: #{cmd}"; exit 1) end def show_help puts "Available commands:" puts " k8s sync [infra|app] - Sync Kubernetes manifests" puts " k8s status - Show status of k8s components" puts " db backup - Perform database backup" puts "\nOptions:" puts " -s, --stg - Use staging environment (default)" puts " -p, --prd - Use production environment" end end # Support end_with? polyfill if needed or use end_with? class String def end_index?(suffix) self.end_with?(suffix) end end Jkctl.new.run