122 lines
3.7 KiB
Go
122 lines
3.7 KiB
Go
package metrics
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||
"k8s.io/client-go/kubernetes"
|
||
"k8s.io/client-go/rest"
|
||
"k8s.io/client-go/tools/clientcmd"
|
||
|
||
"admin-console/internal/model"
|
||
)
|
||
|
||
// K8sClient — обёртка над kubernetes.Clientset для нужд admin-console.
|
||
type K8sClient struct {
|
||
cs *kubernetes.Clientset
|
||
}
|
||
|
||
// NewK8sClient создаёт клиента из in-cluster конфига, с fallback на ~/.kube/config.
|
||
func NewK8sClient() (*K8sClient, error) {
|
||
cfg, err := rest.InClusterConfig()
|
||
if err != nil {
|
||
cfg, err = clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("no k8s config: %w", err)
|
||
}
|
||
}
|
||
cs, err := kubernetes.NewForConfig(cfg)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &K8sClient{cs: cs}, nil
|
||
}
|
||
|
||
// ListClientNamespaces возвращает namespace-ы с лейблом managed-by=fission-console.
|
||
// Для каждого считает количество объектов Fission CRD через Dynamic клиент
|
||
// (пока упрощённо — только через secrets и labels).
|
||
func (k *K8sClient) ListClientNamespaces(ctx context.Context) ([]model.NamespaceInfo, error) {
|
||
nsList, err := k.cs.CoreV1().Namespaces().List(ctx, metav1.ListOptions{
|
||
LabelSelector: "managed-by=fission-console",
|
||
})
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
result := make([]model.NamespaceInfo, 0, len(nsList.Items))
|
||
for _, ns := range nsList.Items {
|
||
info := model.NamespaceInfo{
|
||
Name: ns.Name,
|
||
UserEmail: ns.Labels["user"],
|
||
CreatedAt: ns.CreationTimestamp.Format(time.RFC3339),
|
||
}
|
||
// TODO: добавить подсчёт Fission CRD через dynamic client
|
||
result = append(result, info)
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// CollectUsage возвращает агрегированную статистику по всем клиентским namespace-ам.
|
||
func (k *K8sClient) CollectUsage(ctx context.Context) ([]model.UsageEntry, error) {
|
||
nsList, err := k.cs.CoreV1().Namespaces().List(ctx, metav1.ListOptions{
|
||
LabelSelector: "managed-by=fission-console",
|
||
})
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
now := time.Now().UTC()
|
||
startOfMonth := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, time.UTC)
|
||
|
||
result := make([]model.UsageEntry, 0, len(nsList.Items))
|
||
for _, ns := range nsList.Items {
|
||
entry := model.UsageEntry{
|
||
Namespace: ns.Name,
|
||
UserEmail: ns.Labels["user"],
|
||
PeriodStart: startOfMonth.Format("2006-01-02"),
|
||
PeriodEnd: now.Format("2006-01-02"),
|
||
}
|
||
// TODO: заполнить реальными данными из Prometheus / metrics-server / S3
|
||
result = append(result, entry)
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// ListUsers собирает пользователей из secret fission-console-users в каждом
|
||
// клиентском namespace.
|
||
func (k *K8sClient) ListUsers(ctx context.Context) ([]model.UserInfo, error) {
|
||
nsList, err := k.cs.CoreV1().Namespaces().List(ctx, metav1.ListOptions{
|
||
LabelSelector: "managed-by=fission-console",
|
||
})
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var users []model.UserInfo
|
||
for _, ns := range nsList.Items {
|
||
// label user содержит email владельца namespace
|
||
email := ns.Labels["user"]
|
||
if email == "" {
|
||
// fallback: попробовать прочитать из secret
|
||
secret, err := k.cs.CoreV1().Secrets(ns.Name).Get(ctx, "fission-console-users", metav1.GetOptions{})
|
||
if err == nil {
|
||
for key := range secret.Data {
|
||
if strings.Contains(key, "@") {
|
||
email = key
|
||
break
|
||
}
|
||
}
|
||
}
|
||
}
|
||
users = append(users, model.UserInfo{
|
||
Email: email,
|
||
Namespace: ns.Name,
|
||
CreatedAt: ns.CreationTimestamp.Format(time.RFC3339),
|
||
})
|
||
}
|
||
return users, nil
|
||
}
|