harden(selectel): защита пагинации от неподвижного offset, тест New, документирование disabled

This commit is contained in:
2026-07-03 13:13:24 +07:00
parent 9de5d4712c
commit 70f9bc6793
2 changed files with 122 additions and 0 deletions
+18
View File
@@ -106,6 +106,10 @@ func (c *Client) ListZones(ctx context.Context, creds provider.Credentials) ([]p
if page.NextOffset == 0 || len(page.Result) == 0 {
break
}
if page.NextOffset <= offset {
// API returned a non-advancing offset; stop instead of looping forever.
break
}
offset = page.NextOffset
}
return zones, nil
@@ -136,6 +140,10 @@ func (c *Client) listRRSets(ctx context.Context, token, zoneID string) ([]apiRRS
if page.NextOffset == 0 || len(page.Result) == 0 {
break
}
if page.NextOffset <= offset {
// API returned a non-advancing offset; stop instead of looping forever.
break
}
offset = page.NextOffset
}
return all, nil
@@ -192,6 +200,12 @@ func (c *Client) ApplyChanges(ctx context.Context, creds provider.Credentials, z
return nil
}
// NOTE: disabled-записи не участвуют в diff и не сохраняются при apply, т.к. PATCH
// заменяет весь набор records; поддержка отключённых записей — вне текущей фазы.
// Selectel PATCH /rrset/{id} полностью заменяет набор records: он не мержит переданные
// значения с уже существующими. toRecord отбрасывает disabled-записи при чтении, а
// toRRSet никогда их не восстанавливает при записи — поэтому round-trip (read -> diff
// -> apply) безвозвратно теряет ранее disabled-записи в rrset, если он был изменён.
func toRecord(rr apiRRSet) model.Record {
vals := make([]string, 0, len(rr.Records))
for _, r := range rr.Records {
@@ -203,6 +217,10 @@ func toRecord(rr apiRRSet) model.Record {
return model.Record{Type: model.RecordType(rr.Type), Name: rr.Name, TTL: rr.TTL, Values: vals}
}
// NOTE: disabled-записи не участвуют в diff и не сохраняются при apply, т.к. PATCH
// заменяет весь набор records; поддержка отключённых записей — вне текущей фазы.
// toRRSet всегда шлёт записи без поля disabled, т.е. любой ранее disabled контент,
// отсутствующий в model.Record.Values, будет молча потерян при следующем PATCH этого rrset.
func toRRSet(rec model.Record) apiRRSet {
rs := apiRRSet{Name: rec.Name, Type: string(rec.Type), TTL: rec.TTL}
for _, v := range rec.Values {