fix: fixup multi problem
ci-api / test (push) Successful in 25m0s
ci-web / test (push) Failing after 7m59s

This commit is contained in:
Nuxgrid
2026-07-30 19:29:57 +02:00
parent 0848d3c0a0
commit ac8bdbcab7
14 changed files with 492 additions and 163 deletions
@@ -17,6 +17,7 @@ import (
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
metricsclient "k8s.io/metrics/pkg/client/clientset/versioned"
)
// HelmProvisioner : implémente Provisioner en appelant l'exécutable Helm.
@@ -24,7 +25,8 @@ import (
type HelmProvisioner struct {
cfg *config.Config
k8sClient *kubernetes.Clientset
chartsDir string // chemin vers le dossier des charts (ex: /charts)
metricsClient *metricsclient.Clientset // optionnel : nil si metrics-server indisponible
chartsDir string // chemin vers le dossier des charts (ex: /charts)
frontendImage string
backendImage string
baseDomain string
@@ -34,9 +36,11 @@ type HelmProvisioner struct {
// NewHelmProvisioner crée un nouveau provisioner Helm.
// chartsDir : chemin absolu vers le dossier contenant les charts (backend/, frontend/)
// helmPath : chemin vers l'exécutable helm (optionnel, default: "helm")
// metricsClient : client metrics.k8s.io pour l'usage CPU/mémoire live (optionnel, peut être nil).
func NewHelmProvisioner(
cfg *config.Config,
k8sClient *kubernetes.Clientset,
metricsClient *metricsclient.Clientset,
chartsDir string,
helmPath string,
) (*HelmProvisioner, error) {
@@ -55,6 +59,7 @@ func NewHelmProvisioner(
return &HelmProvisioner{
cfg: cfg,
k8sClient: k8sClient,
metricsClient: metricsClient,
chartsDir: chartsDir,
frontendImage: cfg.FrontendImage,
backendImage: cfg.BackendImage,
@@ -336,6 +341,38 @@ func (h *HelmProvisioner) waitForRollout(namespace string) error {
}
}
// componentResourceLimits : limites CPU/mémoire configurées dans chaque chart
// (deploy/chart-gestion/*/values.yaml), utilisées pour calculer un % d'usage.
var componentResourceLimits = map[string]struct {
cpuMilli int64
memMi int64
}{
"backend": {cpuMilli: 500, memMi: 256},
"frontend": {cpuMilli: 200, memMi: 128},
"postgresql": {cpuMilli: 1000, memMi: 1024},
"redis": {cpuMilli: 500, memMi: 512},
}
// componentKey identifie le composant (backend/frontend/postgresql/redis) à
// partir du nom de pod généré par Helm (ex: "demo-xxx-backend-...-6d59f4-abcde").
func componentKey(podName string) string {
switch {
case strings.Contains(podName, "backend"):
return "backend"
case strings.Contains(podName, "frontend"):
return "frontend"
case strings.Contains(podName, "postgresql"):
return "postgresql"
case strings.Contains(podName, "redis"):
return "redis"
default:
return ""
}
}
// GetResourceState : phase + usage CPU/mémoire live de chaque pod d'une démo.
// L'usage (metrics-server) est best-effort : s'il est indisponible, seule la
// phase du pod est renseignée plutôt que de faire échouer tout l'appel.
func (h *HelmProvisioner) GetResourceState(
ctx context.Context,
namespace string,
@@ -349,23 +386,47 @@ func (h *HelmProvisioner) GetResourceState(
return ResourceState{}, err
}
usage := map[string][2]int64{} // pod name -> [cpuMilli, memMi]
if h.metricsClient != nil {
if list, err := h.metricsClient.MetricsV1beta1().PodMetricses(namespace).List(ctx, metav1.ListOptions{}); err == nil {
for _, m := range list.Items {
var cpu, mem int64
for _, c := range m.Containers {
cpu += c.Usage.Cpu().MilliValue()
mem += c.Usage.Memory().Value() / (1024 * 1024)
}
usage[m.Name] = [2]int64{cpu, mem}
}
}
}
var state ResourceState
for _, pod := range pods.Items {
status := string(pod.Status.Phase)
key := componentKey(pod.Name)
if key == "" {
continue
}
switch {
case strings.Contains(pod.Name, "backend"):
state.APIState = status
cs := ComponentState{Phase: string(pod.Status.Phase)}
if limits, ok := componentResourceLimits[key]; ok {
cs.CPULimitMilli = limits.cpuMilli
cs.MemoryLimitMi = limits.memMi
}
if u, ok := usage[pod.Name]; ok {
cs.CPUMilli = u[0]
cs.MemoryMi = u[1]
}
case strings.Contains(pod.Name, "frontend"):
state.WebState = status
case strings.Contains(pod.Name, "postgresql"):
state.DBState = status
case strings.Contains(pod.Name, "redis"):
state.DBMemoryState = status
switch key {
case "backend":
state.API = cs
case "frontend":
state.Web = cs
case "postgresql":
state.DB = cs
case "redis":
state.DBM = cs
}
}
+16 -6
View File
@@ -27,11 +27,21 @@ type ExternalResource struct {
func (ExternalResource) TableName() string { return "external_pool" }
type ResourceState struct {
APIState string `gorm:"size:20;not null" json:"api_state"`
WebState string `gorm:"size:20;not null" json:"web_state"`
DBState string `gorm:"size:20;not null" json:"db_state"`
DBMemoryState string `gorm:"size:20;not null" json:"dbm_state"`
// ComponentState : état live d'un composant (pod) d'une démo — phase k8s +
// usage CPU/mémoire actuel rapporté par metrics-server, avec les limites
// configurées dans le chart Helm correspondant (pour calculer un pourcentage).
type ComponentState struct {
Phase string `json:"phase"` // Running, Pending, Failed, Unknown, "" si pod introuvable
CPUMilli int64 `json:"cpu_milli"` // usage CPU actuel, en millicores
CPULimitMilli int64 `json:"cpu_limit_milli"` // limite CPU configurée, en millicores
MemoryMi int64 `json:"memory_mi"` // usage mémoire actuel, en Mi
MemoryLimitMi int64 `json:"memory_limit_mi"` // limite mémoire configurée, en Mi
}
func (ResourceState) TableName() string { return "ressource_state" }
// ResourceState : état live des 4 composants d'une démo.
type ResourceState struct {
API ComponentState `json:"api"`
Web ComponentState `json:"web"`
DB ComponentState `json:"db"`
DBM ComponentState `json:"dbm"`
}
+24 -9
View File
@@ -8,19 +8,18 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
metricsclient "k8s.io/metrics/pkg/client/clientset/versioned"
)
func NewClient(cfg *config.Config) (*kubernetes.Clientset, error) {
var (
k8sCfg *rest.Config
err error
)
func restConfig(cfg *config.Config) (*rest.Config, error) {
if cfg.Kubeconfig != "" {
k8sCfg, err = clientcmd.BuildConfigFromFlags("", cfg.Kubeconfig)
} else {
k8sCfg, err = rest.InClusterConfig()
return clientcmd.BuildConfigFromFlags("", cfg.Kubeconfig)
}
return rest.InClusterConfig()
}
func NewClient(cfg *config.Config) (*kubernetes.Clientset, error) {
k8sCfg, err := restConfig(cfg)
if err != nil {
return nil, fmt.Errorf("k8s config: %w", err)
}
@@ -32,3 +31,19 @@ func NewClient(cfg *config.Config) (*kubernetes.Clientset, error) {
return client, nil
}
// NewMetricsClient crée un client pour l'API metrics.k8s.io (metrics-server),
// utilisée pour lire l'usage CPU/mémoire en direct des pods.
func NewMetricsClient(cfg *config.Config) (*metricsclient.Clientset, error) {
k8sCfg, err := restConfig(cfg)
if err != nil {
return nil, fmt.Errorf("k8s config: %w", err)
}
client, err := metricsclient.NewForConfig(k8sCfg)
if err != nil {
return nil, fmt.Errorf("metrics clientset: %w", err)
}
return client, nil
}