Added new docs and sid blocks
This commit is contained in:
parent
b1d0436452
commit
80571bf47f
20 changed files with 280 additions and 24 deletions
0
.gitignore
vendored
Normal file → Executable file
0
.gitignore
vendored
Normal file → Executable file
46
ARCHITECTURE.md
Normal file → Executable file
46
ARCHITECTURE.md
Normal file → Executable file
|
|
@ -117,6 +117,8 @@ waf-host-02 | instance-c
|
||||||
- sp_antiddos # Использовать антиддос сети в set_real_ip (дефолт: true)
|
- sp_antiddos # Использовать антиддос сети в set_real_ip (дефолт: true)
|
||||||
- real_ip_networks # Кастомные сети для set_real_ip если sp_antiddos = false (inet[])
|
- real_ip_networks # Кастомные сети для set_real_ip если sp_antiddos = false (inet[])
|
||||||
- worker_processes # Количество worker процессов nginx внутри контейнера (дефолт: 4)
|
- worker_processes # Количество worker процессов nginx внутри контейнера (дефолт: 4)
|
||||||
|
- one_container # Все SID в один контейнер (дефолт: true). При false — блочный режим
|
||||||
|
- sid_block # Блоки SID для разбивки по контейнерам: {sid1,sid2}{sid3} (TEXT)
|
||||||
```
|
```
|
||||||
|
|
||||||
**Важно:** `waf_instance` определяет на каком хосте должен работать клиент
|
**Важно:** `waf_instance` определяет на каком хосте должен работать клиент
|
||||||
|
|
@ -260,24 +262,46 @@ main() → initConfig() → connectToDB()
|
||||||
|
|
||||||
4. fetchPTAFResourcesData — получение данных ресурсов из API/кеша
|
4. fetchPTAFResourcesData — получение данных ресурсов из API/кеша
|
||||||
|
|
||||||
5. resolvePortsForClient — загрузка/выделение портов
|
5. one_container = false И sid_block задан?
|
||||||
|
├─ Да → processPTAFClientBlocks (блочный режим, см. ниже)
|
||||||
|
└─ Нет → стандартный режим:
|
||||||
|
|
||||||
|
6. resolvePortsForClient — загрузка/выделение портов
|
||||||
├─ .ports.json существует → переиспользуем порты
|
├─ .ports.json существует → переиспользуем порты
|
||||||
├─ Scale up → сохраняем старые порты, добавляем новые
|
├─ Scale up → сохраняем старые порты, добавляем новые
|
||||||
└─ Нет файла → выделяем все заново
|
└─ Нет файла → выделяем все заново
|
||||||
|
|
||||||
6. generateAngieConfigsWithoutReload — генерация Angie конфига
|
7. generateAngieConfigsWithoutReload — генерация Angie конфига
|
||||||
|
|
||||||
7. setupContainers — генерация конфигов и управление контейнерами
|
8. setupContainers — генерация конфигов и управление контейнерами
|
||||||
├─ Запущен недавно, порты не слушают → пересоздаём
|
├─ Запущен недавно, порты не слушают → пересоздаём
|
||||||
├─ Compose изменился → пересоздаём
|
├─ Compose изменился → пересоздаём
|
||||||
├─ Конфиг изменился → nginx -s reload
|
├─ Конфиг изменился → nginx -s reload
|
||||||
└─ Новый → запускаем (с ожиданием освобождения портов)
|
└─ Новый → запускаем (с ожиданием освобождения портов)
|
||||||
|
|
||||||
8. Проверка портов → reload Angie если нужно
|
9. Проверка портов → reload Angie если нужно
|
||||||
|
|
||||||
9. processDrainingContainers — drain лишних контейнеров
|
10. processDrainingContainers — drain лишних контейнеров
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 4. Блочный режим (one_container = false)
|
||||||
|
|
||||||
|
Используется когда разные группы SID должны обслуживаться разными наборами контейнеров.
|
||||||
|
|
||||||
|
```
|
||||||
|
sid_block = '{10111,10231}{10112}{10113}'
|
||||||
|
|
||||||
|
Блок 1 → SID 10111, 10231 → контейнеры {clientTitle}_block1_AZ3_hn01_a001..a{N}
|
||||||
|
Блок 2 → SID 10112 → контейнеры {clientTitle}_block2_AZ3_hn01_a001..a{N}
|
||||||
|
Блок 3 → SID 10113 → контейнеры {clientTitle}_block3_AZ3_hn01_a001..a{N}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `containers_count` применяется к каждому блоку независимо
|
||||||
|
- Каждый блок получает свои Angie конфиги и nginx конфиги только со своими SID
|
||||||
|
- SID не указанные ни в одном блоке не обрабатываются
|
||||||
|
- Имя контейнера: `{clientTitle}_block{N}_{AZx}_{hn/n}{NN}_a{001}`
|
||||||
|
- Каждый блок обрабатывается через стандартный `resolvePortsForClient` → `generateAngieConfigsWithoutReload` → `setupContainers`
|
||||||
|
|
||||||
### 4. Обработка SW-клиента
|
### 4. Обработка SW-клиента
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
@ -786,7 +810,17 @@ UPDATE client_info SET shm_size = 8 WHERE client_title = 'CLIENT001';
|
||||||
UPDATE apps_settings SET balancing_method = 'least_conn' WHERE l7resourceid = 12345;
|
UPDATE apps_settings SET balancing_method = 'least_conn' WHERE l7resourceid = 12345;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Настроить ptaf_fallback
|
### Включить блочный режим (разбивка SID по контейнерам)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Три блока SID, каждый в своём наборе контейнеров
|
||||||
|
UPDATE client_info
|
||||||
|
SET one_container = false,
|
||||||
|
sid_block = '{10111,10231}{10112}{10113}'
|
||||||
|
WHERE client_title = 'CLIENT001';
|
||||||
|
```
|
||||||
|
|
||||||
|
Формат `sid_block`: `{sid1,sid2}{sid3}{sid4,sid5,sid6}` — каждая пара `{}` это один блок.
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- Пропускать трафик если WAF недоступен
|
-- Пропускать трафик если WAF недоступен
|
||||||
|
|
|
||||||
43
DATABASE_SCHEMA.md
Normal file → Executable file
43
DATABASE_SCHEMA.md
Normal file → Executable file
|
|
@ -102,6 +102,8 @@ WHERE hostname = $1 AND instance = $2;
|
||||||
| `sp_antiddos` | BOOLEAN | Использовать антиддос сети в set_real_ip | `true` |
|
| `sp_antiddos` | BOOLEAN | Использовать антиддос сети в set_real_ip | `true` |
|
||||||
| `real_ip_networks` | INET[] | Кастомные сети для set_real_ip (если sp_antiddos=false) | `{10.0.0.0/8}` |
|
| `real_ip_networks` | INET[] | Кастомные сети для set_real_ip (если sp_antiddos=false) | `{10.0.0.0/8}` |
|
||||||
| `worker_processes` | INTEGER | Количество worker процессов nginx | `4` |
|
| `worker_processes` | INTEGER | Количество worker процессов nginx | `4` |
|
||||||
|
| `one_container` | BOOLEAN | Все SID в один контейнер | `true` |
|
||||||
|
| `sid_block` | TEXT | Блоки SID для разбивки: `{sid1,sid2}{sid3}` | `NULL` |
|
||||||
|
|
||||||
### Используемые запросы
|
### Используемые запросы
|
||||||
|
|
||||||
|
|
@ -121,7 +123,9 @@ SELECT
|
||||||
ptaf_fallback_code,
|
ptaf_fallback_code,
|
||||||
sp_antiddos,
|
sp_antiddos,
|
||||||
real_ip_networks,
|
real_ip_networks,
|
||||||
worker_processes
|
worker_processes,
|
||||||
|
one_container,
|
||||||
|
sid_block
|
||||||
FROM client_info
|
FROM client_info
|
||||||
WHERE waf_instance = $1;
|
WHERE waf_instance = $1;
|
||||||
```
|
```
|
||||||
|
|
@ -144,7 +148,9 @@ SELECT
|
||||||
ptaf_fallback_code,
|
ptaf_fallback_code,
|
||||||
sp_antiddos,
|
sp_antiddos,
|
||||||
real_ip_networks,
|
real_ip_networks,
|
||||||
worker_processes
|
worker_processes,
|
||||||
|
one_container,
|
||||||
|
sid_block
|
||||||
FROM client_info
|
FROM client_info
|
||||||
WHERE client_title = $1;
|
WHERE client_title = $1;
|
||||||
```
|
```
|
||||||
|
|
@ -177,6 +183,12 @@ ALTER TABLE client_info
|
||||||
|
|
||||||
ALTER TABLE client_info
|
ALTER TABLE client_info
|
||||||
ADD COLUMN IF NOT EXISTS worker_processes INTEGER DEFAULT 4;
|
ADD COLUMN IF NOT EXISTS worker_processes INTEGER DEFAULT 4;
|
||||||
|
|
||||||
|
ALTER TABLE client_info
|
||||||
|
ADD COLUMN IF NOT EXISTS one_container BOOLEAN DEFAULT TRUE;
|
||||||
|
|
||||||
|
ALTER TABLE client_info
|
||||||
|
ADD COLUMN IF NOT EXISTS sid_block TEXT DEFAULT NULL;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Ключевые поля
|
### Ключевые поля
|
||||||
|
|
@ -220,7 +232,29 @@ UPDATE client_info SET ptaf_fallback_code = '503' WHERE client_title = 'CLIENT00
|
||||||
UPDATE client_info SET worker_processes = 8 WHERE client_title = 'CLIENT001';
|
UPDATE client_info SET worker_processes = 8 WHERE client_title = 'CLIENT001';
|
||||||
```
|
```
|
||||||
|
|
||||||
#### sp_antiddos / real_ip_networks
|
#### one_container / sid_block
|
||||||
|
|
||||||
|
`one_container = true` (дефолт) — все SID клиента обслуживаются одним набором контейнеров. Стандартный режим.
|
||||||
|
|
||||||
|
`one_container = false` + заполненный `sid_block` — блочный режим. Каждый блок `{}` в `sid_block` создаёт отдельный набор контейнеров только со своими SID.
|
||||||
|
|
||||||
|
**Формат `sid_block`:** `{sid1,sid2}{sid3}{sid4,sid5}` — каждая пара фигурных скобок это один блок.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Три блока: первый с двумя SID, второй и третий с одним
|
||||||
|
UPDATE client_info
|
||||||
|
SET one_container = false,
|
||||||
|
sid_block = '{10111,10231}{10112}{10113}'
|
||||||
|
WHERE client_title = 'CLIENT001';
|
||||||
|
```
|
||||||
|
|
||||||
|
**Именование контейнеров в блочном режиме:**
|
||||||
|
- Блок 1 → `{clientTitle}_block1_AZ3_hn01_a001`
|
||||||
|
- Блок 2 → `{clientTitle}_block2_AZ3_hn01_a001`
|
||||||
|
|
||||||
|
**containers_count** применяется к каждому блоку — если `containers_count=2` и 3 блока → 6 контейнеров суммарно.
|
||||||
|
|
||||||
|
**SID не указанные ни в одном блоке не обрабатываются.**
|
||||||
|
|
||||||
`sp_antiddos = true` (дефолт) — в Angie используются антиддос сети из таблицы `ips` для `set_real_ip_from`.
|
`sp_antiddos = true` (дефолт) — в Angie используются антиддос сети из таблицы `ips` для `set_real_ip_from`.
|
||||||
|
|
||||||
|
|
@ -597,6 +631,9 @@ SELECT client_title FROM client_info WHERE debug = true;
|
||||||
-- Клиенты с индивидуальным образом
|
-- Клиенты с индивидуальным образом
|
||||||
SELECT client_title, docker_image FROM client_info WHERE docker_image IS NOT NULL;
|
SELECT client_title, docker_image FROM client_info WHERE docker_image IS NOT NULL;
|
||||||
|
|
||||||
|
-- Клиенты в блочном режиме
|
||||||
|
SELECT client_title, sid_block FROM client_info WHERE one_container = false;
|
||||||
|
|
||||||
-- Клиенты с нестандартным количеством worker процессов
|
-- Клиенты с нестандартным количеством worker процессов
|
||||||
SELECT client_title, worker_processes FROM client_info WHERE worker_processes != 4;
|
SELECT client_title, worker_processes FROM client_info WHERE worker_processes != 4;
|
||||||
|
|
||||||
|
|
|
||||||
0
LICENSE
Normal file → Executable file
0
LICENSE
Normal file → Executable file
0
README.md
Normal file → Executable file
0
README.md
Normal file → Executable file
0
api.go
Normal file → Executable file
0
api.go
Normal file → Executable file
0
config.go
Normal file → Executable file
0
config.go
Normal file → Executable file
0
containers_part1.go
Normal file → Executable file
0
containers_part1.go
Normal file → Executable file
0
containers_part2.go
Normal file → Executable file
0
containers_part2.go
Normal file → Executable file
8
db.go
Normal file → Executable file
8
db.go
Normal file → Executable file
|
|
@ -99,7 +99,7 @@ func getClientInfoByInstance(db *sql.DB, instance string) ([]ClientInfo, error)
|
||||||
query := `
|
query := `
|
||||||
SELECT containers_count, ptaf_config, client_title, fluent_bit_port, waf_instance,
|
SELECT containers_count, ptaf_config, client_title, fluent_bit_port, waf_instance,
|
||||||
docker_image, docker_image_download, debug, shm_size, ptaf_fallback_code,
|
docker_image, docker_image_download, debug, shm_size, ptaf_fallback_code,
|
||||||
sp_antiddos, real_ip_networks, worker_processes
|
sp_antiddos, real_ip_networks, worker_processes, one_container, sid_block
|
||||||
FROM client_info
|
FROM client_info
|
||||||
WHERE waf_instance = $1
|
WHERE waf_instance = $1
|
||||||
`
|
`
|
||||||
|
|
@ -131,6 +131,8 @@ func getClientInfoByInstance(db *sql.DB, instance string) ([]ClientInfo, error)
|
||||||
&ci.SpAntiddos,
|
&ci.SpAntiddos,
|
||||||
pq.Array(&ci.RealIPNetworks),
|
pq.Array(&ci.RealIPNetworks),
|
||||||
&ci.WorkerProcesses,
|
&ci.WorkerProcesses,
|
||||||
|
&ci.OneContainer,
|
||||||
|
&ci.SidBlock,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("ошибка сканирования строки: %w", err)
|
return nil, fmt.Errorf("ошибка сканирования строки: %w", err)
|
||||||
|
|
@ -217,7 +219,7 @@ func getClientInfoByClientTitle(db *sql.DB, clientTitle string) (*ClientInfo, er
|
||||||
query := `
|
query := `
|
||||||
SELECT containers_count, ptaf_config, client_title, fluent_bit_port, waf_instance,
|
SELECT containers_count, ptaf_config, client_title, fluent_bit_port, waf_instance,
|
||||||
docker_image, docker_image_download, debug, shm_size, ptaf_fallback_code,
|
docker_image, docker_image_download, debug, shm_size, ptaf_fallback_code,
|
||||||
sp_antiddos, real_ip_networks, worker_processes
|
sp_antiddos, real_ip_networks, worker_processes, one_container, sid_block
|
||||||
FROM client_info
|
FROM client_info
|
||||||
WHERE client_title = $1
|
WHERE client_title = $1
|
||||||
`
|
`
|
||||||
|
|
@ -238,6 +240,8 @@ func getClientInfoByClientTitle(db *sql.DB, clientTitle string) (*ClientInfo, er
|
||||||
&clientInfo.SpAntiddos,
|
&clientInfo.SpAntiddos,
|
||||||
pq.Array(&clientInfo.RealIPNetworks),
|
pq.Array(&clientInfo.RealIPNetworks),
|
||||||
&clientInfo.WorkerProcesses,
|
&clientInfo.WorkerProcesses,
|
||||||
|
&clientInfo.OneContainer,
|
||||||
|
&clientInfo.SidBlock,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "does not exist") {
|
if strings.Contains(err.Error(), "does not exist") {
|
||||||
|
|
|
||||||
0
drain.go
Normal file → Executable file
0
drain.go
Normal file → Executable file
0
go.mod
Normal file → Executable file
0
go.mod
Normal file → Executable file
0
go.sum
Normal file → Executable file
0
go.sum
Normal file → Executable file
22
main.go
Normal file → Executable file
22
main.go
Normal file → Executable file
|
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -26,6 +27,22 @@ func updateLockStatus(status string) {
|
||||||
lockStatusFile.WriteString(fmt.Sprintf("%d\n%s\n", os.Getpid(), status))
|
lockStatusFile.WriteString(fmt.Sprintf("%d\n%s\n", os.Getpid(), status))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkDockerAvailable проверяет доступность Docker daemon с таймаутом
|
||||||
|
func checkDockerAvailable() error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
cmd := exec.CommandContext(ctx, "docker", "info", "--format", "{{.ServerVersion}}")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
|
return fmt.Errorf("таймаут подключения к Docker daemon (10 сек) — возможно Docker не запущен")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("ошибка подключения к Docker daemon: %w", err)
|
||||||
|
}
|
||||||
|
log.Printf("✓ Docker daemon доступен, версия: %s", strings.TrimSpace(string(output)))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// ════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════
|
||||||
// Lock-файл для предотвращения параллельного запуска
|
// Lock-файл для предотвращения параллельного запуска
|
||||||
|
|
@ -110,6 +127,11 @@ func main() {
|
||||||
log.Fatalf("Ошибка загрузки конфигурации: %v", err)
|
log.Fatalf("Ошибка загрузки конфигурации: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Проверяем доступность Docker daemon
|
||||||
|
if err := checkDockerAvailable(); err != nil {
|
||||||
|
log.Fatalf("❌ Docker daemon недоступен: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
instanceNames, err := getInstancesByHostname(db, hostname)
|
instanceNames, err := getInstancesByHostname(db, hostname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Ошибка получения instances для hostname %s: %v", hostname, err)
|
log.Fatalf("Ошибка получения instances для hostname %s: %v", hostname, err)
|
||||||
|
|
|
||||||
0
nginx.go
Normal file → Executable file
0
nginx.go
Normal file → Executable file
0
ports.go
Normal file → Executable file
0
ports.go
Normal file → Executable file
117
ptaf_processor.go
Normal file → Executable file
117
ptaf_processor.go
Normal file → Executable file
|
|
@ -81,6 +81,11 @@ func processPTAFClient(
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Если one_container = false — разбиваем SID по блокам и обрабатываем каждый блок отдельно
|
||||||
|
if !clientInfo.OneContainer && clientInfo.SidBlock.Valid && clientInfo.SidBlock.String != "" {
|
||||||
|
return processPTAFClientBlocks(db, config, hostname, instanceName, clientInfo, resourcesData, portAllocator)
|
||||||
|
}
|
||||||
|
|
||||||
// Выделение портов
|
// Выделение портов
|
||||||
resourcePortMap, err := resolvePortsForClient(clientInfo, resourcesData, portAllocator)
|
resourcePortMap, err := resolvePortsForClient(clientInfo, resourcesData, portAllocator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -289,3 +294,115 @@ func checkPortListeningPTAF(host string, port int) bool {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// processPTAFClientBlocks обрабатывает клиента с разбивкой SID по блокам контейнеров
|
||||||
|
// Используется когда one_container = false
|
||||||
|
func processPTAFClientBlocks(
|
||||||
|
db *sql.DB,
|
||||||
|
config Config,
|
||||||
|
hostname, instanceName string,
|
||||||
|
clientInfo ClientInfo,
|
||||||
|
resourcesData []ResourceData,
|
||||||
|
portAllocator *PortAllocator,
|
||||||
|
) bool {
|
||||||
|
blocks, err := parseSidBlocks(clientInfo.SidBlock.String)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("⚠ Ошибка парсинга sid_block для %s: %v", clientInfo.ClientTitle, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(blocks) == 0 {
|
||||||
|
log.Printf("⚠ sid_block пустой для %s", clientInfo.ClientTitle)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf(" → Режим блоков: %d блоков SID", len(blocks))
|
||||||
|
|
||||||
|
// Строим map SID → ResourceData для быстрого поиска
|
||||||
|
sidToResource := make(map[int]ResourceData)
|
||||||
|
for _, res := range resourcesData {
|
||||||
|
sidToResource[res.L7ResourceID] = res
|
||||||
|
}
|
||||||
|
|
||||||
|
success := true
|
||||||
|
for blockIdx, sidList := range blocks {
|
||||||
|
blockNum := blockIdx + 1
|
||||||
|
blockName := fmt.Sprintf("block%d", blockNum)
|
||||||
|
log.Printf(" → Обработка блока %d: SID %v", blockNum, sidList)
|
||||||
|
|
||||||
|
// Собираем ресурсы для этого блока
|
||||||
|
var blockResources []ResourceData
|
||||||
|
for _, sid := range sidList {
|
||||||
|
if res, ok := sidToResource[sid]; ok {
|
||||||
|
blockResources = append(blockResources, res)
|
||||||
|
} else {
|
||||||
|
log.Printf(" ⚠ SID %d не найден в данных ресурсов", sid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(blockResources) == 0 {
|
||||||
|
log.Printf(" ⚠ Нет данных для блока %d, пропускаем", blockNum)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создаём clientInfo для этого блока с модифицированным именем
|
||||||
|
blockClientInfo := clientInfo
|
||||||
|
blockClientInfo.ClientTitle = fmt.Sprintf("%s_%s", clientInfo.ClientTitle, blockName)
|
||||||
|
|
||||||
|
// Выделяем порты для блока
|
||||||
|
blockPortMap, err := resolvePortsForClient(blockClientInfo, blockResources, portAllocator)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf(" ⚠ Ошибка выделения портов для блока %d: %v", blockNum, err)
|
||||||
|
success = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Генерируем конфиги Angie для блока
|
||||||
|
angieChanged, err := generateAngieConfigsWithoutReload(config, blockClientInfo.ClientTitle, blockClientInfo, blockResources, blockPortMap, blockClientInfo.Debug)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf(" ⚠ Ошибка генерации конфигов Angie для блока %d: %v", blockNum, err)
|
||||||
|
success = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Настраиваем контейнеры для блока
|
||||||
|
err = setupContainers(config, blockClientInfo.ClientTitle, &blockClientInfo, blockResources, blockPortMap, hostname)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf(" ⚠ Ошибка настройки контейнеров для блока %d: %v", blockNum, err)
|
||||||
|
success = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload Angie если нужно
|
||||||
|
if angieChanged {
|
||||||
|
allPortsReady := true
|
||||||
|
for _, res := range blockResources {
|
||||||
|
containerPorts, ok := blockPortMap[res.L7ResourceID]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, ports := range containerPorts {
|
||||||
|
for _, dockerPort := range ports.HTTPSPorts {
|
||||||
|
if !checkPortListening("127.0.0.1", dockerPort) {
|
||||||
|
allPortsReady = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !allPortsReady {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !allPortsReady {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if allPortsReady {
|
||||||
|
reloadAngie()
|
||||||
|
} else {
|
||||||
|
log.Printf(" ⚠ Не все порты готовы для блока %d, пропускаем reload Angie", blockNum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf(" ✓ Блок %d обработан успешно", blockNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
return success
|
||||||
|
}
|
||||||
|
|
|
||||||
0
sw_processor.go
Normal file → Executable file
0
sw_processor.go
Normal file → Executable file
2
types.go
Normal file → Executable file
2
types.go
Normal file → Executable file
|
|
@ -56,6 +56,8 @@ type ClientInfo struct {
|
||||||
SpAntiddos bool // Использовать антиддос сети для set_real_ip (дефолт true)
|
SpAntiddos bool // Использовать антиддос сети для set_real_ip (дефолт true)
|
||||||
RealIPNetworks []string // Кастомные сети для set_real_ip если SpAntiddos = false
|
RealIPNetworks []string // Кастомные сети для set_real_ip если SpAntiddos = false
|
||||||
WorkerProcesses int // Количество worker процессов nginx (дефолт 4)
|
WorkerProcesses int // Количество worker процессов nginx (дефолт 4)
|
||||||
|
OneContainer bool // Все SID в один контейнер (дефолт true)
|
||||||
|
SidBlock sql.NullString // Блоки SID для разбивки по контейнерам: {sid1,sid2}{sid3}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Структуры для API ответов
|
// Структуры для API ответов
|
||||||
|
|
|
||||||
40
utils.go
Normal file → Executable file
40
utils.go
Normal file → Executable file
|
|
@ -384,3 +384,43 @@ func buildContainerFullName(clientTitle string, containerNum int, hostname strin
|
||||||
// prefix = "AZ3_hn01", итог: "PSB_CFA_AZ3_hn01_a001"
|
// prefix = "AZ3_hn01", итог: "PSB_CFA_AZ3_hn01_a001"
|
||||||
return fmt.Sprintf("%s_%s_a%03d", clientTitle, prefix, containerNum)
|
return fmt.Sprintf("%s_%s_a%03d", clientTitle, prefix, containerNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseSidBlocks парсит строку вида {10111,10231}{10112} в срезы блоков SID
|
||||||
|
// Возвращает [][]int где каждый элемент — блок SID для одного набора контейнеров
|
||||||
|
func parseSidBlocks(sidBlock string) ([][]int, error) {
|
||||||
|
sidBlock = strings.TrimSpace(sidBlock)
|
||||||
|
if sidBlock == "" {
|
||||||
|
return nil, fmt.Errorf("пустая строка sid_block")
|
||||||
|
}
|
||||||
|
|
||||||
|
var blocks [][]int
|
||||||
|
i := 0
|
||||||
|
for i < len(sidBlock) {
|
||||||
|
if sidBlock[i] != '{' {
|
||||||
|
return nil, fmt.Errorf("ожидался '{' на позиции %d", i)
|
||||||
|
}
|
||||||
|
end := strings.Index(sidBlock[i:], "}")
|
||||||
|
if end < 0 {
|
||||||
|
return nil, fmt.Errorf("не найдена закрывающая '}' начиная с позиции %d", i)
|
||||||
|
}
|
||||||
|
inner := sidBlock[i+1 : i+end]
|
||||||
|
parts := strings.Split(inner, ",")
|
||||||
|
var block []int
|
||||||
|
for _, p := range parts {
|
||||||
|
p = strings.TrimSpace(p)
|
||||||
|
if p == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var sid int
|
||||||
|
if _, err := fmt.Sscanf(p, "%d", &sid); err != nil {
|
||||||
|
return nil, fmt.Errorf("невалидный SID '%s': %w", p, err)
|
||||||
|
}
|
||||||
|
block = append(block, sid)
|
||||||
|
}
|
||||||
|
if len(block) > 0 {
|
||||||
|
blocks = append(blocks, block)
|
||||||
|
}
|
||||||
|
i += end + 1
|
||||||
|
}
|
||||||
|
return blocks, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue