Check different sids and update docs

This commit is contained in:
Magnus Root 2026-06-04 12:05:53 +03:00
parent 80571bf47f
commit 1e0690b061
2 changed files with 104 additions and 1 deletions

View file

@ -44,7 +44,7 @@ SECONDARY_DB_HOST=10.100.13.5
### Назначение
Хранит информацию о хостах/серверах в кластере WAF.
Хранит информацию о хостах/серверах в кластере WAF. Таблица `instances` (старая) переименована в `instances_old` и не используется — все запросы идут к `instances_new`.
### Структура
@ -89,7 +89,9 @@ WHERE hostname = $1 AND instance = $2;
| Поле | Тип | Описание | Пример |
|------|-----|----------|--------|
| `id` | SERIAL | Первичный ключ (автоинкремент) | `1` |
| `client_title` | TEXT | Идентификатор клиента | `CLIENT001` |
| `parent_client_title` | TEXT | Родительский клиент (для grafana_gen) | `NULL` |
| `containers_count` | INTEGER | Количество контейнеров (0 = drain всех) | `3` |
| `ptaf_config` | TEXT | Строка подключения к PTAF-серверу | `{...}` |
| `fluent_bit_port` | INTEGER | Порт Fluent Bit | `24224` |
@ -160,6 +162,14 @@ WHERE client_title = $1;
### Миграции
```sql
-- Переименование PK (таблица была переименована из nodes в client_info)
ALTER TABLE client_info DROP CONSTRAINT nodes_pkey;
ALTER TABLE client_info ADD COLUMN id SERIAL PRIMARY KEY;
-- Добавление parent_client_title для grafana_gen
ALTER TABLE client_info
ADD COLUMN IF NOT EXISTS parent_client_title TEXT DEFAULT NULL;
ALTER TABLE client_info
ADD COLUMN IF NOT EXISTS docker_image TEXT DEFAULT NULL;

View file

@ -6,6 +6,7 @@ import (
"log"
"net"
"os"
"path/filepath"
"strings"
"time"
)
@ -83,9 +84,14 @@ func processPTAFClient(
// Если one_container = false — разбиваем SID по блокам и обрабатываем каждый блок отдельно
if !clientInfo.OneContainer && clientInfo.SidBlock.Valid && clientInfo.SidBlock.String != "" {
// Переход true → false: дренируем обычные контейнеры
cleanupOneContainerContainers(clientInfo.ClientTitle, hostname)
return processPTAFClientBlocks(db, config, hostname, instanceName, clientInfo, resourcesData, portAllocator)
}
// Переход false → true: дренируем блочные контейнеры
cleanupBlockContainers(clientInfo.ClientTitle, hostname)
// Выделение портов
resourcePortMap, err := resolvePortsForClient(clientInfo, resourcesData, portAllocator)
if err != nil {
@ -324,9 +330,12 @@ func processPTAFClientBlocks(
}
success := true
activeBlocks := make(map[string]bool)
for blockIdx, sidList := range blocks {
blockNum := blockIdx + 1
blockName := fmt.Sprintf("block%d", blockNum)
activeBlocks[blockName] = true
log.Printf(" → Обработка блока %d: SID %v", blockNum, sidList)
// Собираем ресурсы для этого блока
@ -404,5 +413,89 @@ func processPTAFClientBlocks(
log.Printf(" ✓ Блок %d обработан успешно", blockNum)
}
// Очистка лишних блоков — drain контейнеров и удаление Angie конфигов
// которые больше не входят в sid_block
cleanupStaleBlocks(clientInfo.ClientTitle, activeBlocks, hostname)
return success
}
// cleanupStaleBlocks дренирует контейнеры и удаляет Angie конфиги
// для блоков которые больше не входят в sid_block
func cleanupStaleBlocks(clientTitle string, activeBlocks map[string]bool, hostname string) {
// Ищем существующие блочные Angie конфиги
pattern := fmt.Sprintf("/etc/angie/http.d/angie-ptaf-%s_block*.conf", clientTitle)
matches, _ := filepath.Glob(pattern)
for _, confFile := range matches {
// Извлекаем blockName из имени файла: angie-ptaf-{clientTitle}_{blockName}.conf
base := filepath.Base(confFile)
base = strings.TrimSuffix(base, ".conf")
base = strings.TrimPrefix(base, fmt.Sprintf("angie-ptaf-%s_", clientTitle))
blockName := base // например "block1"
if activeBlocks[blockName] {
continue
}
// Блок устарел — дренируем контейнеры
log.Printf(" → Удаление устаревшего блока %s для %s", blockName, clientTitle)
blockClientTitle := fmt.Sprintf("%s_%s", clientTitle, blockName)
// Помечаем контейнеры на drain
actualCount := getActualContainerCount(blockClientTitle)
for containerNum := 1; containerNum <= actualCount; containerNum++ {
containerFullName := buildContainerFullName(blockClientTitle, containerNum, hostname)
exists, _ := checkContainerStatus(containerFullName)
if exists {
if err := markContainerForDrain(containerFullName, false); err != nil {
log.Printf(" ⚠ Ошибка пометки контейнера %s: %v", containerFullName, err)
} else {
log.Printf(" ✓ Контейнер %s помечен для drain", containerFullName)
}
}
}
// Помечаем Angie конфиг для drain
if err := markAngieConfigForDrain(blockClientTitle, false); err != nil {
log.Printf(" ⚠ Ошибка пометки Angie конфига для %s: %v", blockClientTitle, err)
} else {
log.Printf(" ✓ Angie конфиг для блока %s помечен для drain", blockName)
}
}
}
// cleanupBlockContainers дренирует все блочные контейнеры клиента
// Используется при переходе one_container: false → true
func cleanupBlockContainers(clientTitle string, hostname string) {
pattern := fmt.Sprintf("/etc/angie/http.d/angie-ptaf-%s_block*.conf", clientTitle)
matches, _ := filepath.Glob(pattern)
if len(matches) == 0 {
return
}
log.Printf(" → Обнаружены блочные конфиги при one_container=true, инициируем drain")
cleanupStaleBlocks(clientTitle, map[string]bool{}, hostname)
}
// cleanupOneContainerContainers дренирует обычный (не блочный) контейнер клиента
// Используется при переходе one_container: true → false
func cleanupOneContainerContainers(clientTitle string, hostname string) {
actualCount := getActualContainerCount(clientTitle)
hasNonBlock := false
for containerNum := 1; containerNum <= actualCount; containerNum++ {
containerFullName := buildContainerFullName(clientTitle, containerNum, hostname)
exists, _ := checkContainerStatus(containerFullName)
if exists {
hasNonBlock = true
if err := markContainerForDrain(containerFullName, false); err != nil {
log.Printf(" ⚠ Ошибка пометки контейнера %s: %v", containerFullName, err)
} else {
log.Printf(" ✓ Контейнер %s помечен для drain (переход в блочный режим)", containerFullName)
}
}
}
if hasNonBlock {
if err := markAngieConfigForDrain(clientTitle, false); err != nil {
log.Printf(" ⚠ Ошибка пометки Angie конфига: %v", err)
}
}
}