chore: update
ci-api / test (push) Successful in 28m2s

This commit is contained in:
Xor290
2026-08-08 18:27:52 +02:00
parent 000ed75313
commit 2f6e5a953d
15 changed files with 939 additions and 42 deletions
+38 -19
View File
@@ -9,6 +9,7 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
metricsclient "k8s.io/metrics/pkg/client/clientset/versioned"
)
const pollInterval = 30 * time.Second
@@ -29,23 +30,31 @@ var badWaitingReasons = map[string]bool{
type podKey struct{ namespace, pod string }
// Watcher scrute périodiquement les pods des namespaces actifs et notifie
// tout changement d'état (sain -> panne, panne -> rétabli). Les destinataires
// (recipients) sont résolus à chaque tick où une notification est nécessaire
// — chaque admin configure son propre webhook Discord / bot Telegram dans
// son profil (voir internal/profile), rien n'est figé au démarrage.
// tout changement d'état (sain -> panne, panne -> rétabli), ainsi que les
// dépassements de seuil CPU/mémoire (voir resources.go) et de remplissage
// des volumes (PVC). Les destinataires (recipients) sont résolus à chaque
// tick où une notification est nécessaire — chaque admin configure son
// propre webhook Discord / bot Telegram dans son profil (voir
// internal/profile), rien n'est figé au démarrage.
type Watcher struct {
k8sClient *kubernetes.Clientset
recipients func(ctx context.Context) ([]Notifier, error)
namespaces func() ([]string, error)
state map[podKey]string // dernière raison de panne connue ("" = sain)
k8sClient *kubernetes.Clientset
metricsClient *metricsclient.Clientset // optionnel : nil désactive les alertes CPU/mémoire (le reste du watcher continue de fonctionner)
recipients func(ctx context.Context) ([]Notifier, error)
namespaces func() ([]string, error)
state map[podKey]string // dernière raison de panne connue ("" = sain)
resourceHigh map[podKey]int // ticks consécutifs au-dessus du seuil CPU/mémoire
pvcHigh map[pvcKey]int // ticks consécutifs au-dessus du seuil de remplissage PVC
}
func NewWatcher(k8sClient *kubernetes.Clientset, recipients func(ctx context.Context) ([]Notifier, error), namespaces func() ([]string, error)) *Watcher {
func NewWatcher(k8sClient *kubernetes.Clientset, metricsClient *metricsclient.Clientset, recipients func(ctx context.Context) ([]Notifier, error), namespaces func() ([]string, error)) *Watcher {
return &Watcher{
k8sClient: k8sClient,
recipients: recipients,
namespaces: namespaces,
state: make(map[podKey]string),
k8sClient: k8sClient,
metricsClient: metricsClient,
recipients: recipients,
namespaces: namespaces,
state: make(map[podKey]string),
resourceHigh: make(map[podKey]int),
pvcHigh: make(map[pvcKey]int),
}
}
@@ -69,6 +78,10 @@ func (w *Watcher) tick(ctx context.Context) {
log.Printf("alerts: liste des démos actives indisponible: %v", err)
return
}
nsSet := make(map[string]bool, len(nsList))
for _, ns := range nsList {
nsSet[ns] = true
}
// Les destinataires ne sont chargés (requête DB) que si une notification
// s'avère nécessaire ce tick-ci, et une seule fois par tick.
@@ -92,6 +105,9 @@ func (w *Watcher) tick(ctx context.Context) {
}
seen := make(map[podKey]bool, len(w.state))
seenHigh := make(map[podKey]bool, len(w.resourceHigh))
nodeNames := make(map[string]bool) // nœuds hébergeant un pod Running d'un namespace actif (voir checkPVCUsage)
for _, ns := range nsList {
pods, err := w.k8sClient.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{})
if err != nil {
@@ -114,7 +130,13 @@ func (w *Watcher) tick(ctx context.Context) {
}
w.state[key] = reason
}
if pod.Status.Phase == corev1.PodRunning && pod.Spec.NodeName != "" {
nodeNames[pod.Spec.NodeName] = true
}
}
w.checkResourceUsage(ctx, loadNotifier, ns, pods.Items, seenHigh)
}
// Pods disparus (démo supprimée, rollout) : on oublie leur état pour ne
@@ -124,12 +146,11 @@ func (w *Watcher) tick(ctx context.Context) {
delete(w.state, key)
}
}
w.clearResourceRecovered(ctx, loadNotifier, seenHigh)
w.checkPVCUsage(ctx, loadNotifier, nsSet, nodeNames)
}
func (w *Watcher) notify(ctx context.Context, notifier Notifier, key podKey, prev, reason string) {
if notifier == nil {
return
}
var msg string
switch {
case prev == "" && reason != "":
@@ -139,9 +160,7 @@ func (w *Watcher) notify(ctx context.Context, notifier Notifier, key podKey, pre
default:
msg = fmt.Sprintf("🔴 [%s] pod %s toujours en erreur : %s", key.namespace, key.pod, reason)
}
if err := notifier.Notify(ctx, msg); err != nil {
log.Printf("alerts: notification échouée: %v", err)
}
w.send(ctx, notifier, msg)
}
// problemReason retourne une raison de panne non vide si le pod est dans un