Skip to content

fix(drift): run one reversed octodns-sync per live provider - #5

Merged
srgvg merged 3 commits into
mainfrom
fix/4-drift-multi-target
Aug 31, 2026
Merged

fix(drift): run one reversed octodns-sync per live provider#5
srgvg merged 3 commits into
mainfrom
fix/4-drift-multi-target

Conversation

@srgvg

@srgvg srgvg commented Aug 31, 2026

Copy link
Copy Markdown
Member

Fixes #4.

Problem

generate_drift_config() reversed each zone's full targets list into a single sources list. For a multi-target zone (e.g. targets: [hcloud, desec]), octoDNS then populated one zone object from every live provider with lenient=False — any record present in more than one provider raises DuplicateRecordException. With a shadow-provider setup (Hetzner mirroring deSEC) every shared record collides, so drift-check could never run for such a repo. The record named in the exception is just whichever shared record loads first.

Fix

One reversed plan per live provider:

  • live_providers(zones) — all live providers across zones, deduplicated, first-appearance order.
  • generate_drift_config(config_path, output_path, provider)provider is now required; the reversed config includes only zones targeting that provider; returns the reversed zones mapping.
  • main() loops octodns-sync once per provider, each with its own temp config. Errors fail fast (rc 2). Drift is aggregated per provider and reported with the provider named. --zone skips providers whose reversed config lacks that zone. rc 0 + "No drift detected" only when every provider is clean.

Single-target repos see identical behavior (one provider, one run).

Tests

  • Existing tests updated for the required provider argument.
  • New: live_providers ordered dedup; per-provider config scoping (zone exclusion + sources content); main() running sync once per provider with distinct config files; drift in one provider → rc 1 naming it; error in first provider → rc 2, no second run.
  • Full suite: 457 passed.

Note: this PR was pushed without a pre-commit human review pause; review on the draft.

generate_drift_config() reversed each zone's full targets list into a
single sources list, so a multi-target zone (e.g. hcloud + desec)
populated one octoDNS zone object from every live provider at once.
octoDNS populates with lenient=False, so any record present in more
than one provider raises DuplicateRecordException — and with a
shadow-provider setup every shared record collides, making drift-check
structurally unusable for such repos.

Now:
- live_providers() collects all live providers across zones
  (first-appearance-ordered, deduplicated)
- generate_drift_config() takes a required provider argument, scopes
  the reversed config to zones targeting that provider, and returns
  the reversed zones mapping
- main() runs octodns-sync once per provider with its own temp config,
  fail-fast rc 2 on error, aggregates drift per provider, and reports
  each drifting provider by name; --zone skips providers that do not
  serve that zone

Fixes #4
@srgvg

srgvg commented Aug 31, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-31T12:00:14.890676Z 06288a0 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7ea2985094

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/octodns_gitops/cli/drift.py Outdated
Comment thread src/octodns_gitops/cli/drift.py Outdated
- A bare `targets:` key loads as None; iterate `or []` like octoDNS's
  own manager instead of crashing with TypeError (P2).
- The --zone provider skip is only decidable when every zone key in the
  provider's reversed config is concrete: a '*'-prefixed key is a
  dynamic zone entry only octoDNS can expand, so with one present the
  run proceeds and octodns-sync applies the zone filter itself (P1 --
  previously --zone on a dynamic-zone config skipped every provider and
  falsely reported "No drift detected").
@srgvg

srgvg commented Aug 31, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fe8a3bac89

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/octodns_gitops/cli/drift.py Outdated
Comment thread src/octodns_gitops/cli/drift.py
Comment thread src/octodns_gitops/cli/drift.py Outdated
…check

Second review round on PR #5, all three findings in the per-provider
config generation:

- Zones not targeting the provider are now kept as inert blockers
  (original sources, targets: []) instead of being dropped: octoDNS
  skips them without populating ("no eligible targets"), but the key
  still subtracts from a dynamic ('*') entry's expansion candidates, so
  an explicit zone that deliberately omits a provider no longer gets
  re-included by that provider's wildcard.
- With every zone key present, the --zone provider precheck is
  unnecessary and is removed: the filter is passed straight to
  octodns-sync, whose IdnaDict lookup normalizes case/IDNA (the raw
  dict membership test here did not) and whose manager expands dynamic
  zones before filtering. A provider not serving the zone now resolves
  to its blocker entry and reports no changes.
- manager.plan_outputs is stripped from the generated configs: it
  writes to a fixed filename, so one run per provider would leave only
  the last provider's plan. Drift reads the sync stderr; file plan
  outputs serve no purpose here.
@srgvg

srgvg commented Aug 31, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: 06288a0e7d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@srgvg
srgvg marked this pull request as ready for review August 31, 2026 11:54
@srgvg
srgvg merged commit 328003c into main Aug 31, 2026
10 checks passed
srgvg added a commit that referenced this pull request Aug 31, 2026
- A bare `targets:` key loads as None; iterate `or []` like octoDNS's
  own manager instead of crashing with TypeError (P2).
- The --zone provider skip is only decidable when every zone key in the
  provider's reversed config is concrete: a '*'-prefixed key is a
  dynamic zone entry only octoDNS can expand, so with one present the
  run proceeds and octodns-sync applies the zone filter itself (P1 --
  previously --zone on a dynamic-zone config skipped every provider and
  falsely reported "No drift detected").
@srgvg
srgvg deleted the fix/4-drift-multi-target branch August 31, 2026 11:55

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 06288a0e7d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +85 to +88
reversed_zones[zone_name] = {
"sources": zone_cfg.get("sources") or [],
"targets": [],
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve filters on dynamic blocker zones

When a dynamic entry not targeting this provider uses glob or regex, rebuilding it with only sources and targets discards that filter. Because octoDNS expands dynamic entries in order and subtracts prior matches, this blocker can consequently expand to every zone and leave a later dynamic entry for the current provider with no candidates—for example, a filtered p1 pattern followed by a filtered p2 pattern—so p2 is never checked and the command can incorrectly report no drift. Copy the dynamic selector fields into the blocker so it excludes exactly the zones selected by the original entry.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

drift-check crashes with DuplicateRecordException on any zone with multiple targets

1 participant