224 lines
4.6 KiB
Go
224 lines
4.6 KiB
Go
package clusters
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"sync"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
// ClusterInfo represents a Kubernetes cluster
|
||
|
|
type ClusterInfo struct {
|
||
|
|
Name string
|
||
|
|
APIServer string
|
||
|
|
Healthy bool
|
||
|
|
LastCheck time.Time
|
||
|
|
Capacity int // Max concurrent tasks
|
||
|
|
Usage int // Current task count
|
||
|
|
}
|
||
|
|
|
||
|
|
// ClusterManager manages multiple K8s clusters
|
||
|
|
type ClusterManager struct {
|
||
|
|
mu sync.RWMutex
|
||
|
|
clusters map[string]*ClusterInfo
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewClusterManager creates a new cluster manager
|
||
|
|
func NewClusterManager() *ClusterManager {
|
||
|
|
return &ClusterManager{
|
||
|
|
clusters: make(map[string]*ClusterInfo),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// RegisterCluster registers a new cluster
|
||
|
|
func (cm *ClusterManager) RegisterCluster(name, apiServer string, capacity int) error {
|
||
|
|
if name == "" || apiServer == "" {
|
||
|
|
return fmt.Errorf("cluster name and API server required")
|
||
|
|
}
|
||
|
|
|
||
|
|
cm.mu.Lock()
|
||
|
|
defer cm.mu.Unlock()
|
||
|
|
|
||
|
|
if _, exists := cm.clusters[name]; exists {
|
||
|
|
return fmt.Errorf("cluster already registered: %s", name)
|
||
|
|
}
|
||
|
|
|
||
|
|
cm.clusters[name] = &ClusterInfo{
|
||
|
|
Name: name,
|
||
|
|
APIServer: apiServer,
|
||
|
|
Healthy: true,
|
||
|
|
LastCheck: time.Now(),
|
||
|
|
Capacity: capacity,
|
||
|
|
Usage: 0,
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// UnregisterCluster removes a cluster
|
||
|
|
func (cm *ClusterManager) UnregisterCluster(name string) error {
|
||
|
|
cm.mu.Lock()
|
||
|
|
defer cm.mu.Unlock()
|
||
|
|
|
||
|
|
if _, exists := cm.clusters[name]; !exists {
|
||
|
|
return fmt.Errorf("cluster not found: %s", name)
|
||
|
|
}
|
||
|
|
|
||
|
|
delete(cm.clusters, name)
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetCluster retrieves cluster info
|
||
|
|
func (cm *ClusterManager) GetCluster(name string) (*ClusterInfo, bool) {
|
||
|
|
cm.mu.RLock()
|
||
|
|
defer cm.mu.RUnlock()
|
||
|
|
|
||
|
|
cluster, exists := cm.clusters[name]
|
||
|
|
return cluster, exists
|
||
|
|
}
|
||
|
|
|
||
|
|
// ListClusters returns all registered clusters
|
||
|
|
func (cm *ClusterManager) ListClusters() map[string]*ClusterInfo {
|
||
|
|
cm.mu.RLock()
|
||
|
|
defer cm.mu.RUnlock()
|
||
|
|
|
||
|
|
result := make(map[string]*ClusterInfo)
|
||
|
|
for name, cluster := range cm.clusters {
|
||
|
|
result[name] = cluster
|
||
|
|
}
|
||
|
|
|
||
|
|
return result
|
||
|
|
}
|
||
|
|
|
||
|
|
// HealthCheck checks cluster health
|
||
|
|
func (cm *ClusterManager) HealthCheck(name string) error {
|
||
|
|
cm.mu.Lock()
|
||
|
|
defer cm.mu.Unlock()
|
||
|
|
|
||
|
|
cluster, exists := cm.clusters[name]
|
||
|
|
if !exists {
|
||
|
|
return fmt.Errorf("cluster not found: %s", name)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Simulate health check (in production, query API server)
|
||
|
|
cluster.Healthy = true
|
||
|
|
cluster.LastCheck = time.Now()
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// MarkUnhealthy marks a cluster as unhealthy
|
||
|
|
func (cm *ClusterManager) MarkUnhealthy(name string) error {
|
||
|
|
cm.mu.Lock()
|
||
|
|
defer cm.mu.Unlock()
|
||
|
|
|
||
|
|
cluster, exists := cm.clusters[name]
|
||
|
|
if !exists {
|
||
|
|
return fmt.Errorf("cluster not found: %s", name)
|
||
|
|
}
|
||
|
|
|
||
|
|
cluster.Healthy = false
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// AllocateTask allocates a task to a cluster
|
||
|
|
func (cm *ClusterManager) AllocateTask(name string) error {
|
||
|
|
cm.mu.Lock()
|
||
|
|
defer cm.mu.Unlock()
|
||
|
|
|
||
|
|
cluster, exists := cm.clusters[name]
|
||
|
|
if !exists {
|
||
|
|
return fmt.Errorf("cluster not found: %s", name)
|
||
|
|
}
|
||
|
|
|
||
|
|
if !cluster.Healthy {
|
||
|
|
return fmt.Errorf("cluster not healthy: %s", name)
|
||
|
|
}
|
||
|
|
|
||
|
|
if cluster.Usage >= cluster.Capacity {
|
||
|
|
return fmt.Errorf("cluster at capacity: %s", name)
|
||
|
|
}
|
||
|
|
|
||
|
|
cluster.Usage++
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// ReleaseTask releases a task from a cluster
|
||
|
|
func (cm *ClusterManager) ReleaseTask(name string) error {
|
||
|
|
cm.mu.Lock()
|
||
|
|
defer cm.mu.Unlock()
|
||
|
|
|
||
|
|
cluster, exists := cm.clusters[name]
|
||
|
|
if !exists {
|
||
|
|
return fmt.Errorf("cluster not found: %s", name)
|
||
|
|
}
|
||
|
|
|
||
|
|
if cluster.Usage > 0 {
|
||
|
|
cluster.Usage--
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// FindBestCluster finds the cluster with most available capacity
|
||
|
|
func (cm *ClusterManager) FindBestCluster() (string, error) {
|
||
|
|
cm.mu.RLock()
|
||
|
|
defer cm.mu.RUnlock()
|
||
|
|
|
||
|
|
var bestCluster string
|
||
|
|
maxCapacity := 0
|
||
|
|
|
||
|
|
for name, cluster := range cm.clusters {
|
||
|
|
if cluster.Healthy {
|
||
|
|
available := cluster.Capacity - cluster.Usage
|
||
|
|
if available > maxCapacity {
|
||
|
|
bestCluster = name
|
||
|
|
maxCapacity = available
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if bestCluster == "" {
|
||
|
|
return "", fmt.Errorf("no healthy clusters available")
|
||
|
|
}
|
||
|
|
|
||
|
|
return bestCluster, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetCapacitySummary returns capacity summary
|
||
|
|
func (cm *ClusterManager) GetCapacitySummary() map[string]interface{} {
|
||
|
|
cm.mu.RLock()
|
||
|
|
defer cm.mu.RUnlock()
|
||
|
|
|
||
|
|
totalCapacity := 0
|
||
|
|
totalUsage := 0
|
||
|
|
healthyCount := 0
|
||
|
|
|
||
|
|
for _, cluster := range cm.clusters {
|
||
|
|
totalCapacity += cluster.Capacity
|
||
|
|
totalUsage += cluster.Usage
|
||
|
|
if cluster.Healthy {
|
||
|
|
healthyCount++
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return map[string]interface{}{
|
||
|
|
"total_capacity": totalCapacity,
|
||
|
|
"total_usage": totalUsage,
|
||
|
|
"healthy_clusters": healthyCount,
|
||
|
|
"total_clusters": len(cm.clusters),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetHealthStatus returns health status for all clusters
|
||
|
|
func (cm *ClusterManager) GetHealthStatus() map[string]bool {
|
||
|
|
cm.mu.RLock()
|
||
|
|
defer cm.mu.RUnlock()
|
||
|
|
|
||
|
|
result := make(map[string]bool)
|
||
|
|
for name, cluster := range cm.clusters {
|
||
|
|
result[name] = cluster.Healthy
|
||
|
|
}
|
||
|
|
|
||
|
|
return result
|
||
|
|
}
|