From 6d1a0b99fcf3cdf521f94633c500d59f820c50ef Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 1 Mar 2026 16:58:47 -0600 Subject: [PATCH] Push jk stuf --- jkctl | 161 +++++++++++++++++++++++++++++ k8s/jam-cloud-infra/namespace.yaml | 4 + k8s/jam-cloud-infra/rabbitmq.yaml | 59 +++++++++++ k8s/jam-cloud-infra/redis.yaml | 46 +++++++++ k8s/jam-cloud/namespace.yaml | 4 + 5 files changed, 274 insertions(+) create mode 100755 jkctl create mode 100644 k8s/jam-cloud-infra/namespace.yaml create mode 100644 k8s/jam-cloud-infra/rabbitmq.yaml create mode 100644 k8s/jam-cloud-infra/redis.yaml create mode 100644 k8s/jam-cloud/namespace.yaml diff --git a/jkctl b/jkctl new file mode 100755 index 0000000..b5bc1b3 --- /dev/null +++ b/jkctl @@ -0,0 +1,161 @@ +#!/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}") + + # 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 diff --git a/k8s/jam-cloud-infra/namespace.yaml b/k8s/jam-cloud-infra/namespace.yaml new file mode 100644 index 0000000..bab476a --- /dev/null +++ b/k8s/jam-cloud-infra/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: jam-cloud-infra diff --git a/k8s/jam-cloud-infra/rabbitmq.yaml b/k8s/jam-cloud-infra/rabbitmq.yaml new file mode 100644 index 0000000..4a09e3e --- /dev/null +++ b/k8s/jam-cloud-infra/rabbitmq.yaml @@ -0,0 +1,59 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: rabbitmq + namespace: jam-cloud-infra +spec: + replicas: 1 + selector: + matchLabels: + app: rabbitmq + template: + metadata: + labels: + app: rabbitmq + spec: + containers: + - name: rabbitmq + image: rabbitmq:3.12-management-alpine + ports: + - containerPort: 5672 + name: amqp + - containerPort: 15672 + name: management + env: + - name: RABBITMQ_DEFAULT_USER + value: guest + - name: RABBITMQ_DEFAULT_PASS + value: guest + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi +--- +apiVersion: v1 +kind: Service +metadata: + name: rabbitmq + namespace: jam-cloud-infra + annotations: + external-dns.alpha.kubernetes.io/hostname: rabbitmq.staging.video.jamkazam.com +spec: + type: LoadBalancer + loadBalancerSourceRanges: + - 72.14.184.26/32 + - 173.255.192.5/32 + selector: + app: rabbitmq + ports: + - protocol: TCP + port: 5672 + targetPort: 5672 + name: amqp + - protocol: TCP + port: 15672 + targetPort: 15672 + name: management diff --git a/k8s/jam-cloud-infra/redis.yaml b/k8s/jam-cloud-infra/redis.yaml new file mode 100644 index 0000000..58fc573 --- /dev/null +++ b/k8s/jam-cloud-infra/redis.yaml @@ -0,0 +1,46 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis + namespace: jam-cloud-infra +spec: + replicas: 1 + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + spec: + containers: + - name: redis + image: redis:7.2-alpine + ports: + - containerPort: 6379 + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi +--- +apiVersion: v1 +kind: Service +metadata: + name: redis + namespace: jam-cloud-infra + annotations: + external-dns.alpha.kubernetes.io/hostname: redis.staging.video.jamkazam.com +spec: + type: LoadBalancer + loadBalancerSourceRanges: + - 72.14.184.26/32 + - 173.255.192.5/32 + selector: + app: redis + ports: + - protocol: TCP + port: 6379 + targetPort: 6379 diff --git a/k8s/jam-cloud/namespace.yaml b/k8s/jam-cloud/namespace.yaml new file mode 100644 index 0000000..b1eb9f1 --- /dev/null +++ b/k8s/jam-cloud/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: jam-cloud