Skip to content

Use runtime_data and validate connection at setup for dnsip#169745

Merged
joostlek merged 11 commits into
home-assistant:devfrom
Phil-Rad:dnsip/runtime-data
May 18, 2026
Merged

Use runtime_data and validate connection at setup for dnsip#169745
joostlek merged 11 commits into
home-assistant:devfrom
Phil-Rad:dnsip/runtime-data

Conversation

@Phil-Rad
Copy link
Copy Markdown
Contributor

@Phil-Rad Phil-Rad commented May 4, 2026

Breaking change

Proposed change

Split out from #169688 per @gjohansson-ST's request.

Refactors dnsip so that DNS resolvers are created once at config-entry setup rather than per-entity:

  • async_setup_entry constructs both IPv4 and IPv6 aiodns.DNSResolver instances, runs a single test query against the configured hostname, and raises ConfigEntryNotReady on TimeoutError / DNSError.
  • Both resolvers are attached to entry.runtime_data via a DnsIPRuntimeData dataclass and a DnsIPConfigEntry = ConfigEntry[DnsIPRuntimeData] type alias.
  • Resolvers are explicitly closed on setup failure and on unload to avoid socket leaks.
  • WanIpSensor consumes the resolver from runtime_data instead of constructing its own.
  • Tests updated: patches now point at homeassistant.components.dnsip.aiodns.DNSResolver (since DNSResolver construction now happens in __init__.py); redundant patches in periodic-update sections removed; new tests added for the setup-time DNS error path and the IPv6-only setup branch.

Required so #169688 can claim runtime-data: done and test-before-setup: done in the quality scale.

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Deprecation (breaking change to happen in the future)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

  • This PR is related to issue: Add bronze quality scale to DNS IP #169688

  • This PR fixes or closes issue: fixes #

  • This PR is related to issue:

  • Link to documentation pull request:

  • Link to developer documentation pull request:

  • Link to frontend pull request:

Checklist

  • I understand the code I am submitting and can explain how it works.
  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • I have followed the perfect PR recommendations
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works.
  • Any generated code has been carefully reviewed for correctness and compliance with project standards.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • For the updated dependencies a diff between library versions and ideally a link to the changelog/release notes is added to the PR description.

To help with the load of incoming pull requests:

@home-assistant
Copy link
Copy Markdown
Contributor

home-assistant Bot commented May 4, 2026

Hey there @gjohansson-ST, mind taking a look at this pull request as it has been labeled with an integration (dnsip) you are listed as a code owner for? Thanks!

Code owner commands

Code owners of dnsip can trigger bot actions by commenting:

  • @home-assistant close Closes the pull request.
  • @home-assistant mark-draft Mark the pull request as draft.
  • @home-assistant ready-for-review Remove the draft status from the pull request.
  • @home-assistant rename Awesome new title Renames the pull request.
  • @home-assistant reopen Reopen the pull request.
  • @home-assistant unassign dnsip Removes the current integration label and assignees on the pull request, add the integration domain after the command.
  • @home-assistant update-branch Update the pull request branch with the base branch.
  • @home-assistant add-label needs-more-information Add a label (needs-more-information, problem in dependency, problem in custom component, problem in config, problem in device, feature-request) to the pull request.
  • @home-assistant remove-label needs-more-information Remove a label (needs-more-information, problem in dependency, problem in custom component, problem in config, problem in device, feature-request) on the pull request.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Refactors the dnsip integration to create and validate aiodns.DNSResolver instances once during config entry setup, store them in entry.runtime_data, and re-use them from the sensors (with accompanying test updates).

Changes:

  • Add DnsIPRuntimeData / DnsIPConfigEntry and construct IPv4/IPv6 resolvers during async_setup_entry, including a setup-time DNS validation query and cleanup on unload.
  • Update WanIpSensor to consume resolvers from entry.runtime_data rather than creating/closing its own resolver.
  • Adjust and extend tests to patch resolver construction in homeassistant.components.dnsip and cover setup-time DNS failure + IPv6-only setup.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
homeassistant/components/dnsip/init.py Creates resolvers at config-entry setup, performs a setup-time DNS probe, stores runtime_data, and closes resolvers on unload.
homeassistant/components/dnsip/sensor.py Switches sensors to use entry.runtime_data resolvers and removes per-entity resolver lifecycle handling.
tests/components/dnsip/test_sensor.py Updates patch targets and removes now-redundant resolver re-patching during periodic updates.
tests/components/dnsip/test_init.py Updates patch targets and adds tests for setup-time DNS error handling and IPv6-only setup.

Comment thread homeassistant/components/dnsip/__init__.py
Comment thread homeassistant/components/dnsip/__init__.py
Copy link
Copy Markdown
Member

@gjohansson-ST gjohansson-ST left a comment

Choose a reason for hiding this comment

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

Take a look at the copilot comments too as they seem valid

Comment thread homeassistant/components/dnsip/__init__.py Outdated
Comment thread homeassistant/components/dnsip/sensor.py
@Phil-Rad
Copy link
Copy Markdown
Contributor Author

Phil-Rad commented May 5, 2026

Applied all the requested changes, copilot too.

  • async_migrate_entry now uses DnsIPConfigEntry.
  • WanIpSensor.async_update keeps the _closed recreate path and the await self.resolver.close() on errors.
  • async_setup_entry now probes each enabled address family in parallel via asyncio.gather and only raises ConfigEntryNotReady if every enabled lookup errors (or if the whole thing times out).
  • async_forward_entry_setups is wrapped so resolvers close on platform-setup failure too.

Added two tests for the new branches (test_setup_dns_timeout, test_setup_forward_failure); coverage stays at 100% on the integration. All local gates green.

Will wait and see if any further corrections needed including copilots suggestions

@Phil-Rad Phil-Rad marked this pull request as ready for review May 5, 2026 03:38
Copilot AI review requested due to automatic review settings May 5, 2026 03:38
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 10 comments.

Comment on lines 121 to 122
def create_dns_resolver(self) -> None:
"""Create the DNS resolver."""
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The recreated resolver isn't closed on unload so small leak. But, keeping the recreate path because @gjohansson-ST asked for it earlier. Happy to drop it if you'd prefer no leak over no recreate.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We could use this
or we simply skip self.resolver and use the ones from runtime_data directly

Comment thread tests/components/dnsip/test_sensor.py
Comment thread tests/components/dnsip/test_init.py
Comment thread tests/components/dnsip/test_init.py
Comment thread tests/components/dnsip/test_init.py
Comment thread tests/components/dnsip/test_init.py
Comment thread tests/components/dnsip/test_init.py
Comment thread tests/components/dnsip/test_init.py
Comment thread tests/components/dnsip/test_init.py
Comment thread tests/components/dnsip/test_init.py Outdated
@gjohansson-ST
Copy link
Copy Markdown
Member

Please resolve the comments that has been addressed and if you don't agree with copilot's comments (or mine) you need to respond to those

@gjohansson-ST gjohansson-ST marked this pull request as draft May 5, 2026 10:47
@Phil-Rad Phil-Rad marked this pull request as ready for review May 5, 2026 14:06
Copilot AI review requested due to automatic review settings May 5, 2026 14:06
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment thread homeassistant/components/dnsip/sensor.py Outdated
@Phil-Rad Phil-Rad marked this pull request as draft May 5, 2026 14:14
@Phil-Rad Phil-Rad marked this pull request as ready for review May 6, 2026 00:46
Copilot AI review requested due to automatic review settings May 6, 2026 00:46
@Phil-Rad Phil-Rad requested a review from gjohansson-ST May 7, 2026 07:03
errors = [
result for result in results if isinstance(result, (TimeoutError, DNSError))
]
if errors and len(errors) == len(results):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
if errors and len(errors) == len(results):
if errors and len(errors) == 2:

Let's be specific as we know it's 2

Comment on lines +93 to +97
try:
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
except Exception:
await _close_resolvers()
raise
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What could possibly raise here? And we should not do this anyway.

@home-assistant home-assistant Bot marked this pull request as draft May 7, 2026 17:48
@home-assistant
Copy link
Copy Markdown
Contributor

home-assistant Bot commented May 7, 2026

Please take a look at the requested changes, and use the Ready for review button when you are done, thanks 👍

Learn more about our pull request process.

Copilot AI review requested due to automatic review settings May 9, 2026 03:07
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

Comment on lines +54 to +76
try:
async with asyncio.timeout(10):
results = await asyncio.gather(
resolver_ipv4.query(hostname, "A"),
resolver_ipv6.query(hostname, "AAAA"),
return_exceptions=True,
)
except TimeoutError as err:
await resolver_ipv4.close()
await resolver_ipv6.close()
raise ConfigEntryNotReady(
f"DNS lookup timed out for {hostname}: {err}"
) from err

errors = [
result for result in results if isinstance(result, (TimeoutError, DNSError))
]
if errors and len(errors) == 2:
await resolver_ipv4.close()
await resolver_ipv6.close()
raise ConfigEntryNotReady(
f"DNS lookup failed for {hostname}: {errors[0]}"
) from errors[0]
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Tried that earlier in this PR "let's be specific as we know it's 2" suggestion, so we ended up with always-create-both. Going with the codeowner on this one.

Comment on lines +41 to +52
hostname = entry.data[CONF_HOSTNAME]

resolver_ipv4 = aiodns.DNSResolver(
nameservers=[entry.options[CONF_RESOLVER]],
tcp_port=entry.options[CONF_PORT],
udp_port=entry.options[CONF_PORT],
)
resolver_ipv6 = aiodns.DNSResolver(
nameservers=[entry.options[CONF_RESOLVER_IPV6]],
tcp_port=entry.options[CONF_PORT_IPV6],
udp_port=entry.options[CONF_PORT_IPV6],
)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We had this for a round and reverted after review. Sticking with the always-both pattern you asked for.

Comment on lines +208 to +239
async def test_setup_ipv6_only(hass: HomeAssistant) -> None:
"""Test setup with only IPv6 enabled exercises the IPv6 lookup branch."""

entry = MockConfigEntry(
domain=DOMAIN,
source=SOURCE_USER,
data={
CONF_HOSTNAME: "home-assistant.io",
CONF_NAME: "home-assistant.io",
CONF_IPV4: False,
CONF_IPV6: True,
},
options={
CONF_RESOLVER: "208.67.222.222",
CONF_RESOLVER_IPV6: "2620:119:53::53",
CONF_PORT: 53,
CONF_PORT_IPV6: 53,
},
entry_id="1",
unique_id="home-assistant.io",
)
entry.add_to_hass(hass)

with patch(
"homeassistant.components.dnsip.aiodns.DNSResolver",
side_effect=[RetrieveDNS(), RetrieveDNS()],
):
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()

assert entry.state is ConfigEntryState.LOADED

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fair point about the docstring — it's a bit stale after the revert. Test still verifies the IPv6-only entry loads, just isn't exercising a setup branch anymore

@Phil-Rad Phil-Rad marked this pull request as ready for review May 9, 2026 03:22
@home-assistant home-assistant Bot requested a review from gjohansson-ST May 9, 2026 03:22
Copy link
Copy Markdown
Member

@gjohansson-ST gjohansson-ST left a comment

Choose a reason for hiding this comment

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

Also look at #170048 as it has impact

Comment thread homeassistant/components/dnsip/__init__.py
Comment thread homeassistant/components/dnsip/__init__.py Outdated
Comment thread homeassistant/components/dnsip/sensor.py Outdated
@home-assistant home-assistant Bot marked this pull request as draft May 10, 2026 14:32
@Phil-Rad Phil-Rad marked this pull request as ready for review May 12, 2026 14:02
Copilot AI review requested due to automatic review settings May 12, 2026 14:02
@home-assistant home-assistant Bot requested a review from gjohansson-ST May 12, 2026 14:02
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comment thread homeassistant/components/dnsip/__init__.py
Comment on lines +80 to +85
errors = [
result
for result in results
if isinstance(
result, (TimeoutError, DNSError, AresError, asyncio.CancelledError)
)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not implementing. Suggestion that introduced this filter explicitly included asyncio.CancelledError. Background context of PR #170048, where broader handling of pycares/aiodns leaks treats CancelledError as a leakable exception rather than a true cancellation.

Copy link
Copy Markdown
Member

@gjohansson-ST gjohansson-ST left a comment

Choose a reason for hiding this comment

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

Some very few minor points, looks good otherwise from my side.

Comment thread homeassistant/components/dnsip/sensor.py Outdated
Comment thread tests/components/dnsip/test_init.py Outdated
Comment thread tests/components/dnsip/test_init.py Outdated
Comment thread tests/components/dnsip/test_init.py
@home-assistant home-assistant Bot marked this pull request as draft May 14, 2026 13:27
@Phil-Rad Phil-Rad marked this pull request as ready for review May 15, 2026 03:37
Copilot AI review requested due to automatic review settings May 15, 2026 03:37
@home-assistant home-assistant Bot requested a review from gjohansson-ST May 15, 2026 03:37
@joostlek joostlek dismissed gjohansson-ST’s stale review May 18, 2026 21:24

minor points fixed

@joostlek joostlek merged commit f5d2aa9 into home-assistant:dev May 18, 2026
33 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators May 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants