Skip to content

Commit eb43bef

Browse files
authored
fix: retention and ind deletes (#13)
1 parent ae9a503 commit eb43bef

File tree

14 files changed

+577
-171
lines changed

14 files changed

+577
-171
lines changed

ARCHITECTURE.md

Lines changed: 85 additions & 26 deletions
Large diffs are not rendered by default.

ENVIRONMENT_VARIABLES_AND_FLAGS.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,16 @@ osctl --action=snapshot
9898
| Флаг | Переменная окружения | Описание | Значение по умолчанию |
9999
|------|---------------------|----------|--------------|
100100
| `--retention-threshold` | `RETENTION_THRESHOLD` | Порог использования диска в процентах | `75` |
101+
| `--retention-days-count` | `RETENTION_DAYS_COUNT` | Количество дней для хранения индексов | `2` |
102+
| `--retention-check-snapshots` | `RETENTION_CHECK_SNAPSHOTS` | Проверять наличие валидных снапшотов перед удалением | `true` |
103+
| `--retention-check-nodes-down` | `RETENTION_CHECK_NODES_DOWN` | Проверять выбывшие ноды из кластера перед запуском retention | `true` |
101104
| `--dry-run` | `DRY_RUN` | Показать, какие индексы будут удалены, без удаления | `false` |
102105

103106
**Ключи в конфиг файле:**
104-
- `retention-threshold`
107+
- `retention_threshold`
108+
- `retention_days_count`
109+
- `retention_check_snapshots`
110+
- `retention_check_nodes_down`
105111

106112
### `dereplicator`
107113

@@ -265,7 +271,13 @@ osctl --action=snapshot
265271

266272
| Флаг | Переменная окружения | Описание | Значение по умолчанию |
267273
|------|---------------------|----------|--------------|
274+
| `--indicesdelete-check-snapshots` | `INDICESDELETE_CHECK_SNAPSHOTS` | Проверять наличие валидных снапшотов перед удалением индексов, которые должны иметь снапшоты. Если `true` и не удалось получить информацию о снапшотах или `snap-repo` не настроен, джоба завершается с ошибкой | `true` |
275+
| `--snap-repo` | `SNAPSHOT_REPOSITORY` | Название репозитория для снапшотов (обязателен если `indicesdelete-check-snapshots=true`) | (пусто) |
268276
| `--dry-run` | `DRY_RUN` | Показать удаляемые индексы без удаления | `false` |
269277

278+
**Ключи в конфиг файле:**
279+
- `indicesdelete_check_snapshots`
280+
- `snapshot_repo`
281+
270282
В режиме multitenancy список тенантов берется из `--kibana-tenants-config` (`KIBANA_TENANTS_CONFIG`), файл обязателен.
271283

README.md

Lines changed: 19 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,36 @@
11
# osctl - Инструмент управления жизненным циклом индексов и снапшотов OpenSearch
22

3-
По-умолчанию предполагается использование `config.yaml` в котором описаны параметры запуска. Для автоматического выполнения команд можно использовать параметр `action`.
3+
Ключевые функции в сравнении с iml - время создания снапшотов и индексов зашивается в конец их названия, сами снапшоты и индексы формируются логстешом. Политика ротации настраивается в одном месте конфигом. В конфиге прописываются для каждого интересующего нас префикса своя политика - сколько дней храним в кластере и сколько дней храним снапшоты каждого дня. Все индексы которые не попали в паттерны - управляются политикой unknown. Все делается кронджобами.
44

5-
Все, что указано в конфгиах можно переопределять через флаги, а также большинство команд поддерживают `--dry-run`.
5+
Фактически это замена curator с только нужными нам функциями.
66

7-
## Быстрый старт
7+
Также есть ряд команд, которые решают специфичные проблемы - такие как вычисление оптимального числа шардов или автосоздание индекспаттернов.
88

9-
### Использование с параметром action (рекомендуется)
10-
```bash
11-
# Создать config.yaml с нужной командой
12-
cat > config.yaml << EOF
13-
action: "snapshots"
14-
opensearch_url: "https://your-opensearch:9200"
15-
cert_file: "/path/to/cert.pem"
16-
key_file: "/path/to/key.pem"
17-
EOF
9+
По-умолчанию предполагается использование `config.yaml` в котором описаны параметры запуска и запуск через кронджобы с указанием команды.
1810

19-
# Запустить osctl - команда выполнится автоматически
20-
osctl
21-
osctl --dry-run
22-
```
23-
24-
### Вариант использования через команды и флаги
25-
```bash
26-
osctl snapshots --config=custom_config.yaml --osctl-indices-config=custom_osctl_indices_config.yaml
27-
```
11+
Все, что указано в конфгиах можно переопределять через флаги и переменные, а также все команды поддерживают `--dry-run`.
2812

2913
## Доступные команды (action)
3014

3115
| Команда | Назначение |
3216
|---------|------------|
33-
| `snapshots` | Создание снапшотов согласно конфигурации |
34-
| `snapshotsdelete` | Удаление снапшотов согласно конфигурации |
35-
| `indicesdelete` | Удаление индексов согласно конфигурации |
17+
| `snapshots` | Создание снапшотов согласно политикам в конфиге |
18+
| `snapshotsdelete` | Удаление снапшотов согласно политикам в конфиге |
19+
| `indicesdelete` | Удаление индексов согласно политикам в конфиге |
3620
| `snapshotschecker` | Нахождение отсутствующих снапшотов |
37-
| `snapshot-manual` | Создание снапшотов по флагам |
3821
| `retention` | Удаление индексов со снапшотами при превышении некоторого порога |
39-
| `dereplicator` | Уменьшение числа реплик |
40-
| `coldstorage` | Миграция в холодное хранилище |
22+
| `dereplicator` | Уменьшение числа реплик у индексов со снапшотами |
23+
| `coldstorage` | Миграция в холодное хранилище при превышении числа дней |
4124
| `extracteddelete` | Удаление extracted индексов |
4225
| `danglingchecker` | Проверка dangling индексов |
4326
| `sharding` | Автоматическое выставление оптимального числа шардов |
4427
| `indexpatterns` | Управление index patterns в Kibana |
45-
| `datasource` | Создание Kibana data-source |
28+
| `datasource` | Создание Kibana data-source ( рековерер) |
29+
| `snapshot-manual | Создание только одного снапшота для индексов с определенным паттерном |
4630

4731
## Конфигурация
4832

49-
### Единая конфигурация (`config.yaml`)
33+
### Общая конфигурация (`config.yaml`)
5034

5135
Пример в `config.yaml`
5236

@@ -60,14 +44,14 @@ osctl snapshots --config=custom_config.yaml --osctl-indices-config=custom_osctl_
6044

6145
## Приоритет конфигурации
6246

63-
1. **Флаги командной строки** (наивысший приоритет)
47+
1. **Флаги командной строки** (наибольший приоритет)
6448
2. **Переменные окружения**
65-
3. **Единый конфиг** (`config.yaml`)
49+
3. **Общий конфиг** (`config.yaml`)
6650
4. **Значения по умолчанию** (наименьший приоритет)
6751

6852
## 📚 Документация
6953

70-
- **[ENVIRONMENT_VARIABLES_AND_FLAGS.md](ENVIRONMENT_VARIABLES_AND_FLAGS.md)** - Полный справочник по всем флагам и переменным окружения
71-
- **[ARCHITECTURE.md](ARCHITECTURE.md)** - Подробные алгоритмы и архитектура системы
72-
- **[DEVELOPMENT.md](DEVELOPMENT.md)** - Как вносить изменения и вариант тестирования
73-
- **Встроенная справка**: `osctl [команда] --help`
54+
- **[ENVIRONMENT_VARIABLES_AND_FLAGS.md](ENVIRONMENT_VARIABLES_AND_FLAGS.md)** - Полный список всех флагов и переменных окружения
55+
- **[ARCHITECTURE.md](ARCHITECTURE.md)** - Подробные алгоритмы и архитектура приложения
56+
- **[DEVELOPMENT.md](DEVELOPMENT.md)** - Как вносить изменения и тестировать
57+
- **Справка**: `osctl [команда] --help`

commands/indicesdelete.go

Lines changed: 120 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"osctl/pkg/config"
66
"osctl/pkg/logging"
7+
"osctl/pkg/opensearch"
78
"osctl/pkg/utils"
89
"strings"
910
"time"
@@ -32,8 +33,10 @@ func runIndicesDelete(cmd *cobra.Command, args []string) error {
3233
}
3334

3435
unknownConfig := cfg.GetOsctlIndicesUnknownConfig()
36+
s3Config := cfg.GetOsctlIndicesS3SnapshotsConfig()
37+
checkSnapshots := cfg.GetIndicesDeleteCheckSnapshots()
3538

36-
logger.Info(fmt.Sprintf("Starting indices deletion indicesCount=%d unknownDays=%d", len(indicesConfig), unknownConfig.DaysCount))
39+
logger.Info(fmt.Sprintf("Starting indices deletion indicesCount=%d unknownDays=%d checkSnapshots=%t", len(indicesConfig), unknownConfig.DaysCount, checkSnapshots))
3740

3841
client, err := utils.NewOSClientWithURL(cfg, cfg.GetOpenSearchURL())
3942
if err != nil {
@@ -51,7 +54,8 @@ func runIndicesDelete(cmd *cobra.Command, args []string) error {
5154
logger.Info("Found indices none")
5255
}
5356

54-
var indicesToDelete []string
57+
var indicesOlderThanRetentionPeriod []string
58+
var indicesRequiringSnapshotCheck []string
5559
var unknownIndices []string
5660
var indicesWithoutDateForLog []string
5761

@@ -73,8 +77,21 @@ func runIndicesDelete(cmd *cobra.Command, args []string) error {
7377
}
7478
} else {
7579
if hasDateInName {
76-
if utils.IsOlderThanCutoff(indexName, utils.FormatDate(time.Now().AddDate(0, 0, -indexConfig.DaysCount), cfg.GetDateFormat()), cfg.GetDateFormat()) {
77-
indicesToDelete = append(indicesToDelete, indexName)
80+
cutoffDateDaysCount := utils.FormatDate(time.Now().AddDate(0, 0, -indexConfig.DaysCount), cfg.GetDateFormat())
81+
if utils.IsOlderThanCutoff(indexName, cutoffDateDaysCount, cfg.GetDateFormat()) {
82+
indicesOlderThanRetentionPeriod = append(indicesOlderThanRetentionPeriod, indexName)
83+
84+
if indexConfig.Snapshot {
85+
s3daysCount := s3Config.UnitCount.All
86+
if indexConfig.SnapshotCountS3 > 0 {
87+
s3daysCount = indexConfig.SnapshotCountS3
88+
}
89+
cutoffDateS3 := utils.FormatDate(time.Now().AddDate(0, 0, -s3daysCount), cfg.GetDateFormat())
90+
91+
if !utils.IsOlderThanCutoff(indexName, cutoffDateS3, cfg.GetDateFormat()) {
92+
indicesRequiringSnapshotCheck = append(indicesRequiringSnapshotCheck, indexName)
93+
}
94+
}
7895
}
7996
} else {
8097
indicesWithoutDateForLog = append(indicesWithoutDateForLog, indexName)
@@ -85,8 +102,17 @@ func runIndicesDelete(cmd *cobra.Command, args []string) error {
85102
unknownIndices = utils.FilterUnknownIndices(unknownIndices)
86103
if unknownConfig.DaysCount > 0 {
87104
for _, indexName := range unknownIndices {
88-
if utils.IsOlderThanCutoff(indexName, utils.FormatDate(time.Now().AddDate(0, 0, -unknownConfig.DaysCount), cfg.GetDateFormat()), cfg.GetDateFormat()) {
89-
indicesToDelete = append(indicesToDelete, indexName)
105+
cutoffDateDaysCount := utils.FormatDate(time.Now().AddDate(0, 0, -unknownConfig.DaysCount), cfg.GetDateFormat())
106+
if utils.IsOlderThanCutoff(indexName, cutoffDateDaysCount, cfg.GetDateFormat()) {
107+
indicesOlderThanRetentionPeriod = append(indicesOlderThanRetentionPeriod, indexName)
108+
109+
if unknownConfig.Snapshot {
110+
cutoffDateS3 := utils.FormatDate(time.Now().AddDate(0, 0, -s3Config.UnitCount.Unknown), cfg.GetDateFormat())
111+
112+
if !utils.IsOlderThanCutoff(indexName, cutoffDateS3, cfg.GetDateFormat()) {
113+
indicesRequiringSnapshotCheck = append(indicesRequiringSnapshotCheck, indexName)
114+
}
115+
}
90116
}
91117
}
92118
}
@@ -95,13 +121,89 @@ func runIndicesDelete(cmd *cobra.Command, args []string) error {
95121
logger.Info(fmt.Sprintf("Indices skipped (no date in name) count=%d list=%s", len(indicesWithoutDateForLog), strings.Join(indicesWithoutDateForLog, ", ")))
96122
}
97123

124+
if len(indicesOlderThanRetentionPeriod) > 0 {
125+
logger.Info(fmt.Sprintf("Indices older than retention period (days_count) count=%d list=%s", len(indicesOlderThanRetentionPeriod), strings.Join(indicesOlderThanRetentionPeriod, ", ")))
126+
} else {
127+
logger.Info("Indices older than retention period (days_count): none")
128+
}
129+
130+
if len(indicesRequiringSnapshotCheck) > 0 {
131+
logger.Info(fmt.Sprintf("Indices requiring snapshot check (older than days_count but not older than snapshot_count_s3) count=%d list=%s", len(indicesRequiringSnapshotCheck), strings.Join(indicesRequiringSnapshotCheck, ", ")))
132+
} else {
133+
logger.Info("Indices requiring snapshot check: none")
134+
}
135+
136+
var snapshots []opensearch.Snapshot
137+
var indicesWithoutSnapshot []string
138+
var indicesToDeleteFinal []string
139+
140+
if len(indicesRequiringSnapshotCheck) > 0 {
141+
snapRepo := cfg.GetSnapshotRepo()
142+
if checkSnapshots {
143+
if snapRepo == "" {
144+
return fmt.Errorf("snap-repo is required when indicesdelete-check-snapshots is true")
145+
}
146+
147+
logger.Info(fmt.Sprintf("Getting all snapshots from repository repo=%s", snapRepo))
148+
snapshots, err = utils.GetSnapshotsIgnore404(client, snapRepo, "*")
149+
if err != nil {
150+
return fmt.Errorf("failed to get snapshots: %v", err)
151+
}
152+
if snapshots == nil {
153+
snapshots = []opensearch.Snapshot{}
154+
}
155+
156+
var snapshotNames []string
157+
for _, s := range snapshots {
158+
if s.State == "SUCCESS" {
159+
snapshotNames = append(snapshotNames, s.Snapshot)
160+
}
161+
}
162+
if len(snapshotNames) > 0 {
163+
logger.Info(fmt.Sprintf("Found successful snapshots count=%d", len(snapshotNames)))
164+
} else {
165+
logger.Info("Found snapshots none")
166+
}
167+
168+
for _, indexName := range indicesRequiringSnapshotCheck {
169+
hasSnapshot := utils.HasValidSnapshot(indexName, snapshots)
170+
if hasSnapshot {
171+
logger.Info(fmt.Sprintf("Index has valid snapshot index=%s", indexName))
172+
indicesToDeleteFinal = append(indicesToDeleteFinal, indexName)
173+
} else {
174+
logger.Warn(fmt.Sprintf("Index has no valid snapshot, skipping deletion index=%s", indexName))
175+
indicesWithoutSnapshot = append(indicesWithoutSnapshot, indexName)
176+
}
177+
}
178+
179+
snapshotCheckSet := make(map[string]bool)
180+
for _, idx := range indicesRequiringSnapshotCheck {
181+
snapshotCheckSet[idx] = true
182+
}
183+
184+
for _, indexName := range indicesOlderThanRetentionPeriod {
185+
if !snapshotCheckSet[indexName] {
186+
indicesToDeleteFinal = append(indicesToDeleteFinal, indexName)
187+
}
188+
}
189+
} else {
190+
indicesToDeleteFinal = indicesOlderThanRetentionPeriod
191+
}
192+
} else {
193+
indicesToDeleteFinal = indicesOlderThanRetentionPeriod
194+
}
195+
196+
if len(indicesWithoutSnapshot) > 0 {
197+
logger.Warn(fmt.Sprintf("Indices skipped (no valid snapshot) count=%d list=%s", len(indicesWithoutSnapshot), strings.Join(indicesWithoutSnapshot, ", ")))
198+
}
199+
98200
var successfulDeletions []string
99201
var failedDeletions []string
100202

101-
if len(indicesToDelete) > 0 {
102-
logger.Info(fmt.Sprintf("Indices to delete %s", strings.Join(indicesToDelete, ", ")))
103-
logger.Info(fmt.Sprintf("Deleting indices count=%d", len(indicesToDelete)))
104-
successful, failed, err := utils.BatchDeleteIndices(client, indicesToDelete, cfg.GetDryRun(), logger)
203+
if len(indicesToDeleteFinal) > 0 {
204+
logger.Info(fmt.Sprintf("Indices to delete (final list) count=%d list=%s", len(indicesToDeleteFinal), strings.Join(indicesToDeleteFinal, ", ")))
205+
logger.Info(fmt.Sprintf("Deleting indices count=%d", len(indicesToDeleteFinal)))
206+
successful, failed, err := utils.BatchDeleteIndices(client, indicesToDeleteFinal, cfg.GetDryRun(), logger)
105207
if err != nil {
106208
logger.Error(fmt.Sprintf("Failed to delete indices error=%v", err))
107209
}
@@ -128,7 +230,14 @@ func runIndicesDelete(cmd *cobra.Command, args []string) error {
128230
logger.Info(fmt.Sprintf(" ✗ %s", name))
129231
}
130232
}
131-
if len(successfulDeletions) == 0 && len(failedDeletions) == 0 {
233+
if len(indicesWithoutSnapshot) > 0 {
234+
logger.Info("")
235+
logger.Info(fmt.Sprintf("Skipped (no valid snapshot): %d indices", len(indicesWithoutSnapshot)))
236+
for _, name := range indicesWithoutSnapshot {
237+
logger.Info(fmt.Sprintf(" - %s", name))
238+
}
239+
}
240+
if len(successfulDeletions) == 0 && len(failedDeletions) == 0 && len(indicesWithoutSnapshot) == 0 {
132241
logger.Info("No indices were deleted")
133242
}
134243
logger.Info(strings.Repeat("=", 60))

0 commit comments

Comments
 (0)