build source
This commit is contained in:
commit
ee1fec43ed
4171 changed files with 1351288 additions and 0 deletions
166
internal/k8s/cluster.go
Normal file
166
internal/k8s/cluster.go
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/iliaivanov/spec-kit-remote/cmd/dev-pod-api/internal/model"
|
||||
)
|
||||
|
||||
// nodeMetricsList mirrors the metrics API response for nodes.
|
||||
type nodeMetricsList struct {
|
||||
Items []nodeMetrics `json:"items"`
|
||||
}
|
||||
|
||||
type nodeMetrics struct {
|
||||
Metadata struct {
|
||||
Name string `json:"name"`
|
||||
} `json:"metadata"`
|
||||
Usage map[string]string `json:"usage"`
|
||||
}
|
||||
|
||||
// GetClusterStatus returns node list with capacity, allocatable, and usage.
|
||||
func (c *Client) GetClusterStatus(ctx context.Context) (*model.ClusterStatus, error) {
|
||||
nodes, err := c.Clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list nodes: %w", err)
|
||||
}
|
||||
|
||||
// Try to fetch node metrics (may not be available)
|
||||
metricsMap := c.fetchNodeMetrics(ctx)
|
||||
|
||||
var totalCPUCap, totalCPUAlloc, totalMemCap, totalMemAlloc int64
|
||||
status := &model.ClusterStatus{
|
||||
Nodes: make([]model.NodeStatus, 0, len(nodes.Items)),
|
||||
}
|
||||
|
||||
for _, node := range nodes.Items {
|
||||
ns := model.NodeStatus{
|
||||
Name: node.Name,
|
||||
Status: nodeConditionStatus(node),
|
||||
CPUCapacity: node.Status.Capacity.Cpu().String(),
|
||||
CPUAllocatable: node.Status.Allocatable.Cpu().String(),
|
||||
MemCapacity: node.Status.Capacity.Memory().String(),
|
||||
MemAllocatable: node.Status.Allocatable.Memory().String(),
|
||||
}
|
||||
|
||||
totalCPUCap += node.Status.Capacity.Cpu().MilliValue()
|
||||
totalCPUAlloc += node.Status.Allocatable.Cpu().MilliValue()
|
||||
totalMemCap += node.Status.Capacity.Memory().Value()
|
||||
totalMemAlloc += node.Status.Allocatable.Memory().Value()
|
||||
|
||||
if m, ok := metricsMap[node.Name]; ok {
|
||||
ns.CPUUsage = m.cpuUsage
|
||||
ns.MemUsage = m.memUsage
|
||||
}
|
||||
|
||||
status.Nodes = append(status.Nodes, ns)
|
||||
}
|
||||
|
||||
status.Total = model.ResourceSummary{
|
||||
CPUCapacity: resource.NewMilliQuantity(totalCPUCap, resource.DecimalSI).String(),
|
||||
CPUAllocatable: resource.NewMilliQuantity(totalCPUAlloc, resource.DecimalSI).String(),
|
||||
MemCapacity: resource.NewQuantity(totalMemCap, resource.BinarySI).String(),
|
||||
MemAllocatable: resource.NewQuantity(totalMemAlloc, resource.BinarySI).String(),
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
type nodeUsage struct {
|
||||
cpuUsage string
|
||||
memUsage string
|
||||
}
|
||||
|
||||
func (c *Client) fetchNodeMetrics(ctx context.Context) map[string]nodeUsage {
|
||||
result := make(map[string]nodeUsage)
|
||||
|
||||
restClient := c.Clientset.Discovery().RESTClient()
|
||||
if restClient == nil {
|
||||
return result
|
||||
}
|
||||
|
||||
data, err := restClient.Get().
|
||||
AbsPath("/apis/metrics.k8s.io/v1beta1/nodes").
|
||||
DoRaw(ctx)
|
||||
if err != nil {
|
||||
return result
|
||||
}
|
||||
|
||||
var metrics nodeMetricsList
|
||||
if err := json.Unmarshal(data, &metrics); err != nil {
|
||||
return result
|
||||
}
|
||||
|
||||
for _, m := range metrics.Items {
|
||||
nu := nodeUsage{}
|
||||
if cpu, ok := m.Usage["cpu"]; ok {
|
||||
nu.cpuUsage = cpu
|
||||
}
|
||||
if mem, ok := m.Usage["memory"]; ok {
|
||||
nu.memUsage = mem
|
||||
}
|
||||
result[m.Metadata.Name] = nu
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func nodeConditionStatus(node corev1.Node) string {
|
||||
for _, cond := range node.Status.Conditions {
|
||||
if cond.Type == corev1.NodeReady {
|
||||
if cond.Status == corev1.ConditionTrue {
|
||||
return "Ready"
|
||||
}
|
||||
return "NotReady"
|
||||
}
|
||||
}
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
// cacheServices maps friendly names to PVC names in the dev-infra namespace.
|
||||
var cacheServices = []struct {
|
||||
name string
|
||||
pvcName string
|
||||
}{
|
||||
{"verdaccio", "verdaccio-storage"},
|
||||
{"athens", "athens-storage"},
|
||||
{"cargo-proxy", "cargo-proxy-cache"},
|
||||
}
|
||||
|
||||
// GetCacheStats returns PVC status for cache services in dev-infra.
|
||||
func (c *Client) GetCacheStats(ctx context.Context) ([]model.CacheStat, error) {
|
||||
stats := make([]model.CacheStat, 0, len(cacheServices))
|
||||
|
||||
for _, svc := range cacheServices {
|
||||
pvc, err := c.Clientset.CoreV1().PersistentVolumeClaims("dev-infra").Get(ctx, svc.pvcName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
stats = append(stats, model.CacheStat{
|
||||
Name: svc.name,
|
||||
PVCName: svc.pvcName,
|
||||
Status: "NotFound",
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
capacity := ""
|
||||
if pvc.Status.Capacity != nil {
|
||||
if storage, ok := pvc.Status.Capacity[corev1.ResourceStorage]; ok {
|
||||
capacity = storage.String()
|
||||
}
|
||||
}
|
||||
|
||||
stats = append(stats, model.CacheStat{
|
||||
Name: svc.name,
|
||||
PVCName: svc.pvcName,
|
||||
Capacity: capacity,
|
||||
Status: string(pvc.Status.Phase),
|
||||
})
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue