Docker check playbooks added
This commit is contained in:
parent
ff1a3528c0
commit
efb980d47f
3 changed files with 520 additions and 3 deletions
|
|
@ -56,6 +56,7 @@ var (
|
|||
cfgNginxConfigGlob = "/home/install/conf/ptaf-nginx/*/*/*/*.conf"
|
||||
cfgStateFilePath = "/tmp/port_checker_state.json"
|
||||
cfgServiceStateFilePath = "/tmp/auspex_service_state.json"
|
||||
cfgDockerStateFilePath = "/tmp/auspex_docker_state.json"
|
||||
)
|
||||
|
||||
// --- Параметры проверки портов ---
|
||||
|
|
@ -183,6 +184,9 @@ func applyConfig(env map[string]string) {
|
|||
if v, ok := env["SERVICE_STATE_FILE_PATH"]; ok && v != "" {
|
||||
cfgServiceStateFilePath = v
|
||||
}
|
||||
if v, ok := env["DOCKER_STATE_FILE_PATH"]; ok && v != "" {
|
||||
cfgDockerStateFilePath = v
|
||||
}
|
||||
if v, ok := env["LOG_DIR"]; ok && v != "" {
|
||||
cfgLogDir = v
|
||||
}
|
||||
|
|
|
|||
499
dockercheck.go
Normal file
499
dockercheck.go
Normal file
|
|
@ -0,0 +1,499 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ── Типы ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
type dcContainerInfo struct {
|
||||
ID string
|
||||
Name string
|
||||
Status string // running, exited, dead, restarting, paused, created
|
||||
Health string // healthy, unhealthy, starting, none
|
||||
Image string
|
||||
}
|
||||
|
||||
type dcStateFile struct {
|
||||
Timestamp string `json:"timestamp"`
|
||||
Containers map[string]dcContainerState `json:"containers"` // name → state
|
||||
}
|
||||
|
||||
type dcContainerState struct {
|
||||
Status string `json:"status"`
|
||||
Health string `json:"health"`
|
||||
}
|
||||
|
||||
type dcChangeKind string
|
||||
|
||||
const (
|
||||
dcKindStatusChanged dcChangeKind = "status_changed"
|
||||
dcKindHealthChanged dcChangeKind = "health_changed"
|
||||
dcKindDisappeared dcChangeKind = "disappeared"
|
||||
)
|
||||
|
||||
type dcStateChange struct {
|
||||
Name string
|
||||
Kind dcChangeKind
|
||||
PrevState dcContainerState
|
||||
CurrState dcContainerState
|
||||
Image string
|
||||
}
|
||||
|
||||
func (ch dcStateChange) isProblem() bool {
|
||||
switch ch.Kind {
|
||||
case dcKindDisappeared:
|
||||
return true
|
||||
case dcKindStatusChanged:
|
||||
return ch.CurrState.Status != "running"
|
||||
case dcKindHealthChanged:
|
||||
return ch.CurrState.Health == "unhealthy"
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (ch dcStateChange) isRecovery() bool {
|
||||
switch ch.Kind {
|
||||
case dcKindStatusChanged:
|
||||
return ch.CurrState.Status == "running" && ch.PrevState.Status != "running"
|
||||
case dcKindHealthChanged:
|
||||
return ch.CurrState.Health == "healthy" && ch.PrevState.Health == "unhealthy"
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ── Точка входа ───────────────────────────────────────────────────────────────
|
||||
|
||||
func runDockerCheck() error {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
hostname = "unknown"
|
||||
log.Printf("⚠️ Не удалось получить hostname: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("🔍 DockerCheck запущен на хосте: %s", hostname)
|
||||
|
||||
// Проверяем доступность docker
|
||||
if err := dcCheckDockerAvailable(); err != nil {
|
||||
return fmt.Errorf("docker недоступен: %v", err)
|
||||
}
|
||||
|
||||
// Получаем текущий список контейнеров
|
||||
containers, err := dcGetContainers()
|
||||
if err != nil {
|
||||
return fmt.Errorf("ошибка получения списка контейнеров: %v", err)
|
||||
}
|
||||
|
||||
if len(containers) == 0 {
|
||||
log.Println("ℹ️ Запущенных контейнеров не найдено")
|
||||
}
|
||||
|
||||
log.Printf("📦 Найдено контейнеров: %d", len(containers))
|
||||
|
||||
// Выводим текущее состояние
|
||||
for _, c := range containers {
|
||||
emoji := dcStatusEmoji(c.Status, c.Health)
|
||||
if c.Health != "" && c.Health != "none" {
|
||||
log.Printf("%s %-30s | %-12s | health: %s | %s", emoji, c.Name, c.Status, c.Health, c.Image)
|
||||
} else {
|
||||
log.Printf("%s %-30s | %-12s | %s", emoji, c.Name, c.Status, c.Image)
|
||||
}
|
||||
}
|
||||
|
||||
// Загружаем предыдущее состояние
|
||||
prevState := dcLoadState()
|
||||
|
||||
// Строим map текущего состояния
|
||||
current := make(map[string]dcContainerInfo, len(containers))
|
||||
for _, c := range containers {
|
||||
current[c.Name] = c
|
||||
}
|
||||
|
||||
// Сравниваем
|
||||
changes := dcCompareStates(prevState.Containers, current)
|
||||
|
||||
// Статистика
|
||||
runningCount := 0
|
||||
problemCount := 0
|
||||
for _, c := range containers {
|
||||
if c.Status == "running" && c.Health != "unhealthy" {
|
||||
runningCount++
|
||||
} else {
|
||||
problemCount++
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("\n📊 Итоги проверки контейнеров:")
|
||||
log.Printf(" ✅ Работают нормально: %d", runningCount)
|
||||
log.Printf(" ❌ Проблемных: %d", problemCount)
|
||||
log.Printf(" 🔄 Изменений статуса: %d", len(changes))
|
||||
|
||||
if len(changes) > 0 {
|
||||
log.Println("\n🔄 Изменения с последней проверки:")
|
||||
log.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
||||
for _, ch := range changes {
|
||||
arrow := dcChangeArrow(ch)
|
||||
switch ch.Kind {
|
||||
case dcKindDisappeared:
|
||||
log.Printf(" %s %-30s | исчез (был: %s)", arrow, ch.Name, ch.PrevState.Status)
|
||||
case dcKindStatusChanged:
|
||||
log.Printf(" %s %-30s | %s → %s", arrow, ch.Name, ch.PrevState.Status, ch.CurrState.Status)
|
||||
case dcKindHealthChanged:
|
||||
log.Printf(" %s %-30s | health: %s → %s", arrow, ch.Name, ch.PrevState.Health, ch.CurrState.Health)
|
||||
}
|
||||
}
|
||||
log.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
||||
|
||||
if isTelegramConfigured() {
|
||||
dcSendAlert(hostname, changes)
|
||||
} else {
|
||||
log.Println("\nℹ️ Telegram уведомления отключены (не настроены данные)")
|
||||
}
|
||||
} else {
|
||||
if len(prevState.Containers) == 0 {
|
||||
log.Println("\nℹ️ Первый запуск — состояние сохранено, уведомления при изменениях")
|
||||
} else {
|
||||
log.Println("\n✅ Изменений нет — уведомления не отправляются")
|
||||
}
|
||||
}
|
||||
|
||||
// Сохраняем текущее состояние
|
||||
dcSaveState(current)
|
||||
|
||||
log.Println("=== Проверка контейнеров завершена ===")
|
||||
return nil
|
||||
}
|
||||
|
||||
// ── Получение контейнеров ────────────────────────────────────────────────────
|
||||
|
||||
func dcCheckDockerAvailable() error {
|
||||
err := exec.Command("docker", "info").Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("docker info завершился с ошибкой: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dcGetContainers() ([]dcContainerInfo, error) {
|
||||
// Получаем все контейнеры (включая не-running) через Go-шаблон
|
||||
// Поля: ID, Name, Status, Health, Image — разделитель |
|
||||
out, err := exec.Command(
|
||||
"docker", "ps", "-a",
|
||||
"--format", "{{.ID}}|{{.Names}}|{{.Status}}|{{.Health}}|{{.Image}}",
|
||||
).Output()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("docker ps завершился с ошибкой: %v", err)
|
||||
}
|
||||
|
||||
var containers []dcContainerInfo
|
||||
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
parts := strings.SplitN(line, "|", 5)
|
||||
if len(parts) != 5 {
|
||||
continue
|
||||
}
|
||||
|
||||
c := dcContainerInfo{
|
||||
ID: strings.TrimSpace(parts[0]),
|
||||
Name: strings.TrimSpace(parts[1]),
|
||||
Image: strings.TrimSpace(parts[4]),
|
||||
Health: strings.ToLower(strings.TrimSpace(parts[3])),
|
||||
}
|
||||
|
||||
// Status из docker ps выглядит как "Up 2 hours" или "Exited (1) 3 minutes ago"
|
||||
// Нормализуем до короткого статуса
|
||||
c.Status = dcNormalizeStatus(parts[2])
|
||||
|
||||
containers = append(containers, c)
|
||||
}
|
||||
|
||||
return containers, nil
|
||||
}
|
||||
|
||||
// dcNormalizeStatus приводит вывод docker ps к короткому статусу
|
||||
func dcNormalizeStatus(raw string) string {
|
||||
raw = strings.TrimSpace(raw)
|
||||
lower := strings.ToLower(raw)
|
||||
switch {
|
||||
case strings.HasPrefix(lower, "up"):
|
||||
return "running"
|
||||
case strings.HasPrefix(lower, "exited"):
|
||||
return "exited"
|
||||
case strings.HasPrefix(lower, "restarting"):
|
||||
return "restarting"
|
||||
case strings.HasPrefix(lower, "dead"):
|
||||
return "dead"
|
||||
case strings.HasPrefix(lower, "paused"):
|
||||
return "paused"
|
||||
case strings.HasPrefix(lower, "created"):
|
||||
return "created"
|
||||
case strings.HasPrefix(lower, "removal"):
|
||||
return "removing"
|
||||
default:
|
||||
return lower
|
||||
}
|
||||
}
|
||||
|
||||
// ── Сравнение состояний ──────────────────────────────────────────────────────
|
||||
|
||||
func dcCompareStates(prev map[string]dcContainerState, curr map[string]dcContainerInfo) []dcStateChange {
|
||||
var changes []dcStateChange
|
||||
|
||||
// Проверяем текущие контейнеры
|
||||
for name, currInfo := range curr {
|
||||
prevInfo, existed := prev[name]
|
||||
|
||||
if !existed {
|
||||
// Новый контейнер — не алертим, просто добавится в state
|
||||
continue
|
||||
}
|
||||
|
||||
currState := dcContainerState{Status: currInfo.Status, Health: currInfo.Health}
|
||||
|
||||
// Изменение статуса
|
||||
if prevInfo.Status != currState.Status {
|
||||
changes = append(changes, dcStateChange{
|
||||
Name: name,
|
||||
Kind: dcKindStatusChanged,
|
||||
PrevState: prevInfo,
|
||||
CurrState: currState,
|
||||
Image: currInfo.Image,
|
||||
})
|
||||
}
|
||||
|
||||
// Изменение health (независимо от статуса)
|
||||
if prevInfo.Health != currState.Health &&
|
||||
currState.Health != "" && currState.Health != "none" &&
|
||||
prevInfo.Health != "" && prevInfo.Health != "none" {
|
||||
changes = append(changes, dcStateChange{
|
||||
Name: name,
|
||||
Kind: dcKindHealthChanged,
|
||||
PrevState: prevInfo,
|
||||
CurrState: currState,
|
||||
Image: currInfo.Image,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Проверяем исчезнувшие контейнеры (были в state, нет сейчас)
|
||||
for name, prevInfo := range prev {
|
||||
if _, exists := curr[name]; !exists {
|
||||
changes = append(changes, dcStateChange{
|
||||
Name: name,
|
||||
Kind: dcKindDisappeared,
|
||||
PrevState: prevInfo,
|
||||
CurrState: dcContainerState{},
|
||||
Image: "",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return changes
|
||||
}
|
||||
|
||||
// ── State-файл ────────────────────────────────────────────────────────────────
|
||||
|
||||
func dcLoadState() dcStateFile {
|
||||
data, err := os.ReadFile(cfgDockerStateFilePath)
|
||||
if err != nil {
|
||||
return dcStateFile{Containers: make(map[string]dcContainerState)}
|
||||
}
|
||||
|
||||
var state dcStateFile
|
||||
if err := json.Unmarshal(data, &state); err != nil {
|
||||
log.Printf("⚠️ Не удалось прочитать state-файл docker: %v", err)
|
||||
return dcStateFile{Containers: make(map[string]dcContainerState)}
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
func dcSaveState(current map[string]dcContainerInfo) {
|
||||
containers := make(map[string]dcContainerState, len(current))
|
||||
for name, info := range current {
|
||||
containers[name] = dcContainerState{
|
||||
Status: info.Status,
|
||||
Health: info.Health,
|
||||
}
|
||||
}
|
||||
|
||||
state := dcStateFile{
|
||||
Timestamp: time.Now().Format("2006-01-02 15:04:05"),
|
||||
Containers: containers,
|
||||
}
|
||||
|
||||
data, err := json.MarshalIndent(state, "", " ")
|
||||
if err != nil {
|
||||
log.Printf("⚠️ Ошибка сериализации state-файла docker: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.WriteFile(cfgDockerStateFilePath, data, 0644); err != nil {
|
||||
log.Printf("⚠️ Ошибка записи state-файла %s: %v", cfgDockerStateFilePath, err)
|
||||
}
|
||||
}
|
||||
|
||||
// ── Отправка алерта ──────────────────────────────────────────────────────────
|
||||
|
||||
func dcSendAlert(hostname string, changes []dcStateChange) {
|
||||
var problems, recoveries []dcStateChange
|
||||
for _, ch := range changes {
|
||||
if ch.isRecovery() {
|
||||
recoveries = append(recoveries, ch)
|
||||
} else if ch.isProblem() {
|
||||
problems = append(problems, ch)
|
||||
}
|
||||
}
|
||||
|
||||
// Если нет ни проблем, ни восстановлений — молчим
|
||||
if len(problems) == 0 && len(recoveries) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Заголовок по наиболее критичному событию
|
||||
var header string
|
||||
switch {
|
||||
case len(problems) > 0 && len(recoveries) > 0:
|
||||
header = "⚠️ <b>Изменения в состоянии контейнеров</b>"
|
||||
case len(problems) > 0:
|
||||
header = "🚨 <b>АЛЕРТ: Проблемы с контейнерами!</b>"
|
||||
default:
|
||||
header = "✅ <b>Контейнеры восстановлены</b>"
|
||||
}
|
||||
|
||||
message := fmt.Sprintf(
|
||||
"%s\n\n"+
|
||||
"🖥 <b>Хост:</b> <code>%s</code>\n"+
|
||||
"⏰ <b>Время:</b> %s\n\n",
|
||||
header,
|
||||
hostname,
|
||||
time.Now().Format("2006-01-02 15:04:05"),
|
||||
)
|
||||
|
||||
// Проблемные контейнеры
|
||||
if len(problems) > 0 {
|
||||
message += "━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"
|
||||
message += fmt.Sprintf("❌ <b>Проблемы (%d):</b>\n\n", len(problems))
|
||||
for _, ch := range problems {
|
||||
emoji := dcChangeEmoji(ch)
|
||||
switch ch.Kind {
|
||||
case dcKindDisappeared:
|
||||
message += fmt.Sprintf(
|
||||
"%s <b>%s</b>\n"+
|
||||
" 📌 Контейнер исчез (был: <code>%s</code>)\n\n",
|
||||
emoji, ch.Name, ch.PrevState.Status,
|
||||
)
|
||||
case dcKindStatusChanged:
|
||||
message += fmt.Sprintf(
|
||||
"%s <b>%s</b>\n"+
|
||||
" 📌 <code>%s</code> → <code>%s</code>\n"+
|
||||
" 🐳 <code>%s</code>\n"+
|
||||
" 🔧 <code>docker logs %s --tail 50</code>\n\n",
|
||||
emoji, ch.Name,
|
||||
ch.PrevState.Status, ch.CurrState.Status,
|
||||
ch.Image, ch.Name,
|
||||
)
|
||||
case dcKindHealthChanged:
|
||||
message += fmt.Sprintf(
|
||||
"%s <b>%s</b>\n"+
|
||||
" 📌 health: <code>%s</code> → <code>%s</code>\n"+
|
||||
" 🐳 <code>%s</code>\n"+
|
||||
" 🔧 <code>docker inspect %s</code>\n\n",
|
||||
emoji, ch.Name,
|
||||
ch.PrevState.Health, ch.CurrState.Health,
|
||||
ch.Image, ch.Name,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Восстановленные контейнеры
|
||||
if len(recoveries) > 0 {
|
||||
message += "━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"
|
||||
message += fmt.Sprintf("✅ <b>Восстановились (%d):</b>\n\n", len(recoveries))
|
||||
for _, ch := range recoveries {
|
||||
switch ch.Kind {
|
||||
case dcKindStatusChanged:
|
||||
message += fmt.Sprintf(
|
||||
"✅ <b>%s</b>\n"+
|
||||
" 📌 <code>%s</code> → <code>%s</code>\n\n",
|
||||
ch.Name, ch.PrevState.Status, ch.CurrState.Status,
|
||||
)
|
||||
case dcKindHealthChanged:
|
||||
message += fmt.Sprintf(
|
||||
"✅ <b>%s</b>\n"+
|
||||
" 📌 health: <code>%s</code> → <code>%s</code>\n\n",
|
||||
ch.Name, ch.PrevState.Health, ch.CurrState.Health,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
message += "━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"
|
||||
message += "💡 <i>docker ps -a | docker logs <имя> --tail 50</i>"
|
||||
|
||||
if err := telegramSendHTML(message); err != nil {
|
||||
log.Printf("❌ Ошибка отправки в Telegram: %v", err)
|
||||
} else {
|
||||
log.Println("✅ Алерт успешно отправлен в Telegram")
|
||||
}
|
||||
}
|
||||
|
||||
// ── Вспомогательные функции ──────────────────────────────────────────────────
|
||||
|
||||
func dcStatusEmoji(status, health string) string {
|
||||
if health == "unhealthy" {
|
||||
return "🤒"
|
||||
}
|
||||
switch status {
|
||||
case "running":
|
||||
return "✅"
|
||||
case "restarting":
|
||||
return "🔄"
|
||||
case "exited":
|
||||
return "🔴"
|
||||
case "dead":
|
||||
return "💀"
|
||||
case "paused":
|
||||
return "⏸"
|
||||
default:
|
||||
return "❓"
|
||||
}
|
||||
}
|
||||
|
||||
func dcChangeEmoji(ch dcStateChange) string {
|
||||
switch ch.Kind {
|
||||
case dcKindDisappeared:
|
||||
return "👻"
|
||||
case dcKindHealthChanged:
|
||||
return "🤒"
|
||||
case dcKindStatusChanged:
|
||||
switch ch.CurrState.Status {
|
||||
case "restarting":
|
||||
return "🔄"
|
||||
case "exited":
|
||||
return "🔴"
|
||||
case "dead":
|
||||
return "💀"
|
||||
}
|
||||
}
|
||||
return "❓"
|
||||
}
|
||||
|
||||
func dcChangeArrow(ch dcStateChange) string {
|
||||
if ch.isRecovery() {
|
||||
return "✅"
|
||||
}
|
||||
if ch.isProblem() {
|
||||
return "❌"
|
||||
}
|
||||
return "🔄"
|
||||
}
|
||||
20
main.go
20
main.go
|
|
@ -7,7 +7,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
const appVersion = "1.1.0"
|
||||
const appVersion = "1.2.0"
|
||||
|
||||
// Цвета терминала (общие для всех модулей)
|
||||
const (
|
||||
|
|
@ -86,6 +86,13 @@ func runByFlag(flag string, extraArgs []string) {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
case "-dc", "-d":
|
||||
fmt.Printf("%s🐳 Auspex — Проверка контейнеров Docker%s\n\n", cCyan, cReset)
|
||||
if err := runDockerCheck(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Ошибка: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
case "-v", "--version":
|
||||
fmt.Printf("auspex v%s\n", appVersion)
|
||||
|
||||
|
|
@ -109,9 +116,10 @@ func showMenu() {
|
|||
fmt.Printf(" %s4%s) %s-m %s Проверка совпадений портов Angie ↔ Docker\n", cGreen, cReset, cCyan, cReset)
|
||||
fmt.Printf(" %s5%s) %s-l %s Проверка активности логов (PostgreSQL)\n", cGreen, cReset, cCyan, cReset)
|
||||
fmt.Printf(" %s6%s) %s-sv%s Проверка состояния служб (systemd)\n", cGreen, cReset, cCyan, cReset)
|
||||
fmt.Printf(" %s7%s) %s-dc%s Проверка контейнеров Docker\n", cGreen, cReset, cCyan, cReset)
|
||||
fmt.Printf(" %s0%s) Выход\n\n", cGreen, cReset)
|
||||
fmt.Printf("%sДля быстрого вызова: auspex <ключ>%s\n", cYellow, cReset)
|
||||
fmt.Printf("%sПример: auspex -ss | auspex -c | auspex -p | auspex -m [клиент] | auspex -l | auspex -sv%s\n\n", cYellow, cReset)
|
||||
fmt.Printf("%sПример: auspex -ss | auspex -c | auspex -p | auspex -m [клиент] | auspex -l | auspex -sv | auspex -dc%s\n\n", cYellow, cReset)
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
|
|
@ -154,11 +162,16 @@ func showMenu() {
|
|||
fmt.Fprintf(os.Stderr, "Ошибка: %v\n", err)
|
||||
}
|
||||
return
|
||||
case "7", "-dc", "-d", "dc", "d":
|
||||
if err := runDockerCheck(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Ошибка: %v\n", err)
|
||||
}
|
||||
return
|
||||
case "0", "q", "quit", "exit":
|
||||
fmt.Println("Выход.")
|
||||
return
|
||||
default:
|
||||
fmt.Printf("%sНеизвестная команда. Введите 1-6 или 0 для выхода.%s\n", cRed, cReset)
|
||||
fmt.Printf("%sНеизвестная команда. Введите 1-7 или 0 для выхода.%s\n", cRed, cReset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -184,6 +197,7 @@ func printUsage() {
|
|||
fmt.Printf(" %-12s Проверка совпадений портов Angie ↔ Docker [имя_клиента]\n", "-m")
|
||||
fmt.Printf(" %-12s Проверка активности логов (PostgreSQL)\n", "-l")
|
||||
fmt.Printf(" %-12s Проверка состояния служб systemd\n", "-sv")
|
||||
fmt.Printf(" %-12s Проверка контейнеров Docker\n", "-dc")
|
||||
fmt.Printf(" %-12s Показать версию\n", "-v")
|
||||
fmt.Printf(" %-12s Показать эту справку\n\n", "-h")
|
||||
fmt.Printf("Без аргументов запускается интерактивное меню.\n")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue