Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package main

import (
"context"
"log"
"os"
"os/signal"
"syscall"
"time"

"cosmolet/pkg/config"
"cosmolet/pkg/controller"
"cosmolet/pkg/health"
)

func main() {
log.Println("Starting Cosmolet BGP Service Controller")

// Initialize configuration
cfg := &config.Config{
Namespaces: []string{"ingress-controller"},
LoopIntervalSeconds: 30, // updated field name
BGPMode: "connected",
NodeName: os.Getenv("NODE_NAME"),
NodeIP: os.Getenv("NODE_IP"),
HealthCheckPort: 1042, // make sure this field exists in config.Config
HealthCheckTimeout: 2, // make sure this field exists in config.Config
}

log.Printf("Version: %s, Commit: %s, Build Date: %s", "dev", "unknown", "unknown")
log.Printf("Monitoring namespaces: %v", cfg.Namespaces)
log.Printf("Loop interval: %d seconds", cfg.LoopIntervalSeconds)
log.Printf("BGP mode: %s", cfg.BGPMode)

// Context for graceful shutdown
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// Handle termination signals
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
sig := <-sigs
log.Printf("Received signal: %v", sig)
cancel()
}()

// Start health check server
health.StartHealthServer(":8080")

// Initialize BGP controller
bgpController, err := controller.NewBGPServiceController(cfg, ctx)
if err != nil {
log.Fatalf("Failed to initialize BGP controller: %v", err)
}

log.Println("Running in connected mode: only adding healthy service IPs to loopback, skipping FRR")
if err := bgpController.Start(); err != nil {
log.Fatalf("Controller stopped with error: %v", err)
}
}

2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.21-alpine AS builder
FROM golang:1.24-alpine AS builder

WORKDIR /app

Expand Down
5 changes: 5 additions & 0 deletions charts/cosmolet/templates/daemonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ spec:
mountPath: /etc/cosmolet
- name: frr-sockets
mountPath: /var/run/frr
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
volumes:
- name: config
configMap:
Expand Down
6 changes: 6 additions & 0 deletions charts/cosmolet/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ resources:
cpu: 100m
memory: 128Mi

env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName

nodeSelector: {}
tolerations:
- operator: Exists
Expand Down
113 changes: 28 additions & 85 deletions cmd/cosmolet/main.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// cmd/cosmolet/main.go
package main

import (
Expand All @@ -17,129 +16,73 @@ import (
"cosmolet/pkg/health"
)

const (
defaultConfigPath = "/etc/cosmolet/config.yaml"
defaultLogLevel = "info"
)

var (
configPath = flag.String("config", defaultConfigPath, "Path to configuration file")
logLevel = flag.String("log-level", defaultLogLevel, "Log level (debug, info, warn, error)")
version = flag.Bool("version", false, "Print version information")

// Build information (set via ldflags)
Version = "dev"
GitCommit = "unknown"
BuildDate = "unknown"
configPath = flag.String("config", "/etc/cosmolet/config.yaml", "Path to configuration file")
Version = "dev"
GitCommit = "unknown"
BuildDate = "unknown"
)

func main() {
flag.Parse()
log.Printf("Starting Cosmolet BGP Controller v%s", Version)

if *version {
printVersion()
return
}

log.Printf("Starting Cosmolet BGP Service Controller")
log.Printf("Version: %s, Commit: %s, Build Date: %s", Version, GitCommit, BuildDate)

// Load configuration
cfg, err := config.LoadConfig(*configPath)
if err != nil {
log.Fatalf("Failed to load configuration: %v", err)
log.Fatalf("Failed to load config: %v", err)
}

log.Printf("Configuration loaded from: %s", *configPath)
log.Printf("Monitoring namespaces: %v", cfg.Services.Namespaces)
log.Printf("Loop interval: %d seconds", cfg.LoopIntervalSeconds)
// Auto-detect node name if not provided
cfg.GetNodeName()

// Create context for graceful shutdown
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// Start health check server
healthChecker := health.NewChecker()
go startHealthServer(healthChecker)
hc := health.NewChecker()
go startHealthServer(hc)

// Create and start BGP controller
bgpController, err := controller.NewBGPServiceController(cfg, ctx)
// ✅ FIX: remove extra argument (cfg.NodeName)
bgp, err := controller.NewBGPServiceController(cfg, ctx)
if err != nil {
log.Fatalf("Failed to create BGP service controller: %v", err)
log.Fatalf("Failed to create controller: %v", err)
}

// Start controller in goroutine
go func() {
if err := bgpController.Start(); err != nil {
log.Printf("BGP controller error: %v", err)
if err := bgp.Start(); err != nil {
log.Printf("Controller error: %v", err)
cancel()
}
}()

// Mark as ready
healthChecker.SetReady(true)

// Wait for shutdown signal
hc.SetReady(true)
waitForShutdown(cancel)

log.Println("Shutting down Cosmolet BGP Service Controller")
}

func printVersion() {
fmt.Printf("Cosmolet BGP Service Controller\n")
fmt.Printf("Version: %s\n", Version)
fmt.Printf("Git Commit: %s\n", GitCommit)
fmt.Printf("Build Date: %s\n", BuildDate)
log.Println("Cosmolet stopped")
}

func startHealthServer(checker *health.Checker) {
mux := http.NewServeMux()

// Health endpoints
mux.HandleFunc("/healthz", checker.LivenessHandler)
mux.HandleFunc("/readyz", checker.ReadinessHandler)
mux.HandleFunc("/version", versionHandler)

// Metrics endpoint (basic for now)
mux.HandleFunc("/metrics", metricsHandler)

mux.HandleFunc("/metrics", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(w, "# Cosmolet metrics\n")
})
server := &http.Server{
Addr: ":8080",
Handler: mux,
}

log.Println("Starting health check server on :8080")
log.Println("Health server running on :8080")
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Printf("Health server error: %v", err)
}
}

func versionHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, `{
"version": "%s",
"gitCommit": "%s",
"buildDate": "%s"
}`, Version, GitCommit, BuildDate)
}

func metricsHandler(w http.ResponseWriter, r *http.Request) {
// Basic metrics endpoint - in a real implementation,
// you would use Prometheus client library
w.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(w, "# HELP cosmolet_info Information about cosmolet\n")
fmt.Fprintf(w, "# TYPE cosmolet_info gauge\n")
fmt.Fprintf(w, "cosmolet_info{version=\"%s\",commit=\"%s\"} 1\n", Version, GitCommit)
}

func waitForShutdown(cancel context.CancelFunc) {
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

sig := <-sigChan
log.Printf("Received signal: %s", sig)

// Give some time for graceful shutdown
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
s := <-sig
log.Printf("Received signal: %v", s)
cancel()
time.Sleep(5 * time.Second)
time.Sleep(2 * time.Second)
}

11 changes: 7 additions & 4 deletions examples/basic-config.yaml
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
# Basic configuration for Cosmolet BGP Service Controller
services:
namespaces:
- "default"
- "kube-system"
- "ingress-controller"

loop_interval_seconds: 30
loop_interval_seconds: 3

bgp_mode: "dynamic" # options: "dynamic" or "connected"

bgp:
enabled: true
asn: 65496
excluded_ips:

logging:
level: "info"
format: "text"

frr:
socket_path: "/var/run/frr"

39 changes: 39 additions & 0 deletions examples/cosmolet-access.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: comsolet-cosmolet
namespace: cosmolet
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cosmolet-access
rules:
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cosmolet-access-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cosmolet-access
subjects:
- kind: ServiceAccount
name: comsolet-cosmolet
namespace: cosmolet
Loading