From f28ccce6a020a8c22a29346ee3fc83fdb7f28828 Mon Sep 17 00:00:00 2001 From: Magnus Root Date: Tue, 10 Mar 2026 17:38:08 +0300 Subject: [PATCH] Fix --- dockercheck.go | 63 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/dockercheck.go b/dockercheck.go index b89d180..5640e5b 100644 --- a/dockercheck.go +++ b/dockercheck.go @@ -36,6 +36,7 @@ const ( dcKindStatusChanged dcChangeKind = "status_changed" dcKindHealthChanged dcChangeKind = "health_changed" dcKindDisappeared dcChangeKind = "disappeared" + dcKindAppeared dcChangeKind = "appeared" ) type dcStateChange struct { @@ -58,6 +59,10 @@ func (ch dcStateChange) isProblem() bool { return false } +func (ch dcStateChange) isNew() bool { + return ch.Kind == dcKindAppeared +} + func (ch dcStateChange) isRecovery() bool { switch ch.Kind { case dcKindStatusChanged: @@ -108,6 +113,7 @@ func runDockerCheck() error { // Загружаем предыдущее состояние prevState := dcLoadState() + isFirstRun := len(prevState.Containers) == 0 // Строим map текущего состояния current := make(map[string]dcContainerInfo, len(containers)) @@ -116,7 +122,7 @@ func runDockerCheck() error { } // Сравниваем - changes := dcCompareStates(prevState.Containers, current) + changes := dcCompareStates(prevState.Containers, current, isFirstRun) // Статистика runningCount := 0 @@ -140,6 +146,8 @@ func runDockerCheck() error { for _, ch := range changes { arrow := dcChangeArrow(ch) switch ch.Kind { + case dcKindAppeared: + log.Printf(" %s %-30s | появился (%s) | %s", arrow, ch.Name, ch.CurrState.Status, ch.Image) case dcKindDisappeared: log.Printf(" %s %-30s | исчез (был: %s)", arrow, ch.Name, ch.PrevState.Status) case dcKindStatusChanged: @@ -265,7 +273,7 @@ func dcNormalizeStatus(raw string) string { // ── Сравнение состояний ────────────────────────────────────────────────────── -func dcCompareStates(prev map[string]dcContainerState, curr map[string]dcContainerInfo) []dcStateChange { +func dcCompareStates(prev map[string]dcContainerState, curr map[string]dcContainerInfo, isFirstRun bool) []dcStateChange { var changes []dcStateChange // Проверяем текущие контейнеры @@ -273,7 +281,16 @@ func dcCompareStates(prev map[string]dcContainerState, curr map[string]dcContain prevInfo, existed := prev[name] if !existed { - // Новый контейнер — не алертим, просто добавится в state + if !isFirstRun { + // Новый контейнер появился после первого запуска — алертим + changes = append(changes, dcStateChange{ + Name: name, + Kind: dcKindAppeared, + PrevState: dcContainerState{}, + CurrState: dcContainerState{Status: currInfo.Status, Health: currInfo.Health}, + Image: currInfo.Image, + }) + } continue } @@ -365,29 +382,34 @@ func dcSaveState(current map[string]dcContainerInfo) { // ── Отправка алерта ────────────────────────────────────────────────────────── func dcSendAlert(hostname string, changes []dcStateChange) { - var problems, recoveries []dcStateChange + var problems, recoveries, appeared []dcStateChange for _, ch := range changes { - if ch.isRecovery() { + switch { + case ch.isNew(): + appeared = append(appeared, ch) + case ch.isRecovery(): recoveries = append(recoveries, ch) - } else if ch.isProblem() { + case ch.isProblem(): problems = append(problems, ch) } } - // Если нет ни проблем, ни восстановлений — молчим - if len(problems) == 0 && len(recoveries) == 0 { + // Если нет никаких изменений — молчим + if len(problems) == 0 && len(recoveries) == 0 && len(appeared) == 0 { return } // Заголовок по наиболее критичному событию var header string switch { - case len(problems) > 0 && len(recoveries) > 0: - header = "⚠️ Изменения в состоянии контейнеров" case len(problems) > 0: header = "🚨 АЛЕРТ: Проблемы с контейнерами!" - default: + case len(recoveries) > 0 && len(appeared) > 0: + header = "⚠️ Изменения в состоянии контейнеров" + case len(recoveries) > 0: header = "✅ Контейнеры восстановлены" + default: + header = "ℹ️ Новые контейнеры" } message := fmt.Sprintf( @@ -458,6 +480,20 @@ func dcSendAlert(hostname string, changes []dcStateChange) { } } + // Новые контейнеры + if len(appeared) > 0 { + message += "━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" + message += fmt.Sprintf("🆕 Появились (%d):\n\n", len(appeared)) + for _, ch := range appeared { + message += fmt.Sprintf( + "🆕 %s\n"+ + " 📌 Статус: %s\n"+ + " 🐳 %s\n\n", + ch.Name, ch.CurrState.Status, ch.Image, + ) + } + } + message += "━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" message += "💡 docker ps -a | docker logs <имя> --tail 50" @@ -492,6 +528,8 @@ func dcStatusEmoji(status, health string) string { func dcChangeEmoji(ch dcStateChange) string { switch ch.Kind { + case dcKindAppeared: + return "🆕" case dcKindDisappeared: return "👻" case dcKindHealthChanged: @@ -510,6 +548,9 @@ func dcChangeEmoji(ch dcStateChange) string { } func dcChangeArrow(ch dcStateChange) string { + if ch.isNew() { + return "🆕" + } if ch.isRecovery() { return "✅" }