require "kemal" require "kubernetes" require "prometheus" require "db" require "pg" # Import the ContainerDeployment CRD Kubernetes.import_crd "#{__DIR__}/../k8s/crd-containerdeployment.yaml" module ApiController VERSION = "0.1.0" # Initialize Prometheus metrics REQUESTS = Prometheus.counter("api_requests_total", "Total number of API requests") RESPONSE_TIME = Prometheus.histogram( "api_response_time_seconds", "Response time in seconds", [0.1, 0.3, 0.5, 0.7, 0.9, 1.0] ) # Configure Kubernetes client token_path = "/var/run/secrets/kubernetes.io/serviceaccount/token" cert_path = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" k8s_client = Kubernetes::Client.new( server: URI.parse("https://#{ENV["KUBERNETES_SERVICE_HOST"]? || "localhost"}:#{ENV["KUBERNETES_SERVICE_PORT"]? || "8443"}"), token: File.exists?(token_path) ? File.read(token_path) : "dev-token", certificate_file: File.exists?(cert_path) ? cert_path : "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" ) # Middleware for tracking metrics before_all do |env| REQUESTS.inc env.set "start_time", Time.monotonic.total_seconds.to_f end after_all do |env| if start_time = env.get?("start_time").try(&.as(Float64)) response_time = Time.monotonic.total_seconds.to_f - start_time RESPONSE_TIME.observe(response_time) end end # Health check endpoint get "/health" do {status: "healthy", version: VERSION}.to_json end # Metrics endpoint for Prometheus get "/metrics" do |env| env.response.content_type = "text/plain" Prometheus.collect end # API endpoints for container deployments get "/api/v1/containerdeployments" do |env| begin deployments = k8s_client.containerdeployments(namespace: "default") deployments.to_json rescue ex env.response.status_code = 500 {error: ex.message}.to_json end end post "/api/v1/containerdeployments" do |env| begin deployment_data = JSON.parse(env.request.body.not_nil!) result = k8s_client.apply_containerdeployment( api_version: "app.container.mom/v1alpha1", kind: "ContainerDeployment", metadata: { name: deployment_data["spec"]["name"].as_s, namespace: "default" }, spec: deployment_data["spec"] ) env.response.status_code = 201 result.to_json rescue ex env.response.status_code = 400 {error: ex.message}.to_json end end get "/api/v1/containerdeployments/:name" do |env| begin name = env.params.url["name"] deployment = k8s_client.containerdeployment(name, namespace: "default") deployment.to_json rescue ex env.response.status_code = 404 {error: ex.message}.to_json end end delete "/api/v1/containerdeployments/:name" do |env| begin name = env.params.url["name"] k8s_client.delete_containerdeployment(name, namespace: "default") env.response.status_code = 204 rescue ex env.response.status_code = 404 {error: ex.message}.to_json end end # Watch for ContainerDeployment changes watch_fiber = spawn do k8s_client.watch_containerdeployments("default") do |watch| deployment = watch.object case watch when .added?, .modified? Log.info { "ContainerDeployment #{deployment.metadata.namespace}/#{deployment.metadata.name} updated" } # Handle deployment updates when .deleted? Log.info { "ContainerDeployment #{deployment.metadata.namespace}/#{deployment.metadata.name} deleted" } # Handle deployment deletion end end end # Handle shutdown gracefully Signal::INT.trap do Log.info { "Shutting down API controller..." } watch_fiber.cancel Kemal.stop exit end Signal::TERM.trap do Log.info { "Shutting down API controller..." } watch_fiber.cancel Kemal.stop exit end # Start the server Kemal.config.port = ENV["PORT"]?.try(&.to_i) || 3000 Kemal.run end