Skip to content

feat(pdns): support GetDomainFilter interface#6234

Open
clwluvw wants to merge 4 commits intokubernetes-sigs:masterfrom
clwluvw:pdns-domainfilter
Open

feat(pdns): support GetDomainFilter interface#6234
clwluvw wants to merge 4 commits intokubernetes-sigs:masterfrom
clwluvw:pdns-domainfilter

Conversation

@clwluvw
Copy link

@clwluvw clwluvw commented Feb 27, 2026

What does it do ?

Overrides GetDomainFilter() for PDNSProvider to return real domain filters. before it was returning empty as of the BaseProvider struct.

Motivation

From #6232 (comment)

More

  • Yes, this PR title follows Conventional Commits
  • Yes, I added unit tests
  • Yes, I updated end user documentation accordingly

Signed-off-by: Seena Fallah <seenafallah@gmail.com>
@k8s-ci-robot k8s-ci-robot requested a review from szuecs February 27, 2026 22:25
@k8s-ci-robot k8s-ci-robot added the provider Issues or PRs related to a provider label Feb 27, 2026
@k8s-ci-robot k8s-ci-robot requested a review from vflaux February 27, 2026 22:25
@k8s-ci-robot k8s-ci-robot added cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Feb 27, 2026
@k8s-ci-robot
Copy link
Contributor

Hi @clwluvw. Thanks for your PR.

I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work.

Tip

We noticed you've done this a few times! Consider joining the org to skip this step and gain /lgtm and other bot rights. We recommend asking approvers on your previous PRs to sponsor you.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@k8s-ci-robot k8s-ci-robot added the size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. label Feb 27, 2026
@vflaux
Copy link
Contributor

vflaux commented Feb 27, 2026

/ok-to-test

@k8s-ci-robot k8s-ci-robot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Feb 27, 2026
@clwluvw
Copy link
Author

clwluvw commented Feb 27, 2026

Should we remove domainFilter from PDNSAPIClient{} as well and pass it from PDNSProvider to PartitionZones()?

@ivankatliarchuk
Copy link
Member

ivankatliarchuk commented Mar 2, 2026

Should we remove domainFilter from PDNSAPIClient{} as well and pass it from PDNSProvider to PartitionZones()?

Most likely yes.

I'm not too sure about the state of this PR. Adding this

func (p *PDNSProvider) GetDomainFilter() endpoint.DomainFilterInterface {
	return p.domainFilter
}

What capabilities we are getting? Have you executed a code against a cluster, what is the gain?

@ivankatliarchuk
Copy link
Member

ivankatliarchuk commented Mar 3, 2026

Have a look at this PR #6249. It contains contributor guidlines related to provider domainfilters and blueprints

Signed-off-by: Seena Fallah <seenafallah@gmail.com>
@ivankatliarchuk
Copy link
Member

Worth to execute against infrastructure, add unit tests, and review if case is missing add unit test(s) here too https://github.com/kubernetes-sigs/external-dns/blob/master/endpoint/domain_filter_test.go

Provider manages a.com, b.com. User sets --domain-filter=c.com.

With a dynamic GetDomainFilter() the plan sees:

  MatchAllDomainFilters{
      c.com,
      [a.com, .a.com, b.com, .b.com],
  }

Nothing satisfies both - the plan is empty, the controller does nothing. That is the correct and safe outcome.

When GetDomainFilter() returns &endpoint.DomainFilter{}, the plan sees:

  MatchAllDomainFilters{
      c.com,
      <empty>,   // matches everything — silently fell open
  }

The controller proceeds to reconcile c.com against a provider that may not manage it at all. It's not catastrophic — Records() will return nothing for c.com — but it's a silent fail-open that can cause confusion.

@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. labels Mar 4, 2026
@clwluvw
Copy link
Author

clwluvw commented Mar 4, 2026

@ivankatliarchuk - Could take another look? i hope i get it right.

Signed-off-by: Seena Fallah <seenafallah@gmail.com>
@clwluvw clwluvw force-pushed the pdns-domainfilter branch from a63ccf6 to 8186b49 Compare March 4, 2026 19:59
@coveralls
Copy link

coveralls commented Mar 4, 2026

Pull Request Test Coverage Report for Build 22730895426

Details

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • 45 unchanged lines in 1 file lost coverage.
  • Overall coverage increased (+0.02%) to 79.183%

Files with Coverage Reduction New Missed Lines %
pdns/pdns.go 45 72.9%
Totals Coverage Status
Change from base Build 22540459008: 0.02%
Covered Lines: 16071
Relevant Lines: 20296

💛 - Coveralls

Copy link
Member

@ivankatliarchuk ivankatliarchuk left a comment

Choose a reason for hiding this comment

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

Could you as well share results of smoke test, similar to #5085 (comment)

Signed-off-by: Seena Fallah <seenafallah@gmail.com>
@clwluvw
Copy link
Author

clwluvw commented Mar 5, 2026

Could you as well share results of smoke test, similar to #5085 (comment)

@ivankatliarchuk - Is there a reason why we don't have e2e tests in the project for such cases?

@ivankatliarchuk
Copy link
Member

ivankatliarchuk commented Mar 5, 2026

Resources infra + plus maintainers time we all just volunteers here. We slowly adding end-2-end tests but for coredns #6162. The project owners decided while back to move providers out of tree, due to maintenance complexity of providers reasons.

So at the moment unit test coverage + smoke tests evidences, this is what we rely on. Not ideal, but better then nothing.

@ivankatliarchuk
Copy link
Member

Pull Request Test Coverage Report for Build 22730895426

Details

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • 45 unchanged lines in 1 file lost coverage.
  • Overall coverage increased (+0.02%) to 79.183%

Files with Coverage Reduction New Missed Lines %
pdns/pdns.go 45 72.9%
Totals Coverage Status
Change from base Build 22540459008: 0.02%
Covered Lines: 16071
Relevant Lines: 20296

💛 - Coveralls

Could you have a look. The code coverage shows new missing lines

@clwluvw
Copy link
Author

clwluvw commented Mar 5, 2026

Could you have a look. The code coverage shows new missing lines

@ivankatliarchuk - Hmm, that is weird. in my coverage report, everything misses are unrelated to the changes here:
coverage.html

@clwluvw
Copy link
Author

clwluvw commented Mar 5, 2026

Could you as well share results of smoke test, similar to #5085 (comment)

Tested with this script and tests are passing: e2e.sh

[INFO] === ExternalDNS logs ===
time="2026-03-05T19:21:42Z" level=debug msg="Records fetched:\n[a-app.example.local 300 IN TXT  \"heritage=external-dns,external-dns/owner=external-dns,external-dns/resource=crd/default/test-domain-a\" [] app.example.local 300 IN A  192.168.1.10 [] a-api.example.local 300 IN TXT  \"heritage=external-dns,external-dns/owner=external-dns,external-dns/resource=crd/default/test-domain-a\" [] api.example.local 300 IN A  192.168.1.20 [] example.local 3600 IN SOA  ns1.example.local. hostmaster.example.local. 2026030501 10800 3600 604800 3600 [] example.local 3600 IN NS  ns1.example.local []]"
time="2026-03-05T19:21:42Z" level=debug msg="dedupSource: collecting endpoints and removing duplicates"
time="2026-03-05T19:21:42Z" level=debug msg="multiSource: collecting endpoints from 3 child sources and removing duplicates"
time="2026-03-05T19:21:42Z" level=debug msg="No endpoints could be generated from 'service/default/kubernetes'"
time="2026-03-05T19:21:42Z" level=debug msg="No endpoints could be generated from 'service/external-dns/external-dns'"
time="2026-03-05T19:21:42Z" level=debug msg="No endpoints could be generated from 'service/ingress-nginx/ingress-nginx-controller'"
time="2026-03-05T19:21:42Z" level=debug msg="No endpoints could be generated from 'service/ingress-nginx/ingress-nginx-controller-admission'"
time="2026-03-05T19:21:42Z" level=debug msg="No endpoints could be generated from 'service/kube-system/kube-dns'"
time="2026-03-05T19:21:42Z" level=debug msg="No endpoints could be generated from 'service/powerdns/powerdns'"
time="2026-03-05T19:21:42Z" level=debug msg="ignoring record app.other.local that does not match domain filter"
time="2026-03-05T19:21:42Z" level=debug msg="ignoring record api.other.local that does not match domain filter"
time="2026-03-05T19:21:42Z" level=info msg="All records are already up to date"
time="2026-03-05T19:21:52Z" level=debug msg="Records fetched:\n[a-app.example.local 300 IN TXT  \"heritage=external-dns,external-dns/owner=external-dns,external-dns/resource=crd/default/test-domain-a\" [] app.example.local 300 IN A  192.168.1.10 [] a-api.example.local 300 IN TXT  \"heritage=external-dns,external-dns/owner=external-dns,external-dns/resource=crd/default/test-domain-a\" [] api.example.local 300 IN A  192.168.1.20 [] example.local 3600 IN SOA  ns1.example.local. hostmaster.example.local. 2026030501 10800 3600 604800 3600 [] example.local 3600 IN NS  ns1.example.local []]"
time="2026-03-05T19:21:52Z" level=debug msg="dedupSource: collecting endpoints and removing duplicates"
time="2026-03-05T19:21:52Z" level=debug msg="multiSource: collecting endpoints from 3 child sources and removing duplicates"
time="2026-03-05T19:21:52Z" level=debug msg="No endpoints could be generated from 'service/ingress-nginx/ingress-nginx-controller-admission'"
time="2026-03-05T19:21:52Z" level=debug msg="No endpoints could be generated from 'service/kube-system/kube-dns'"
time="2026-03-05T19:21:52Z" level=debug msg="No endpoints could be generated from 'service/powerdns/powerdns'"
time="2026-03-05T19:21:52Z" level=debug msg="No endpoints could be generated from 'service/default/kubernetes'"
time="2026-03-05T19:21:52Z" level=debug msg="No endpoints could be generated from 'service/external-dns/external-dns'"
time="2026-03-05T19:21:52Z" level=debug msg="No endpoints could be generated from 'service/ingress-nginx/ingress-nginx-controller'"
time="2026-03-05T19:21:52Z" level=debug msg="ignoring record app.other.local that does not match domain filter"
time="2026-03-05T19:21:52Z" level=debug msg="ignoring record api.other.local that does not match domain filter"
time="2026-03-05T19:21:52Z" level=info msg="All records are already up to date"
time="2026-03-05T19:22:03Z" level=debug msg="Records fetched:\n[a-app.example.local 300 IN TXT  \"heritage=external-dns,external-dns/owner=external-dns,external-dns/resource=crd/default/test-domain-a\" [] app.example.local 300 IN A  192.168.1.10 [] a-api.example.local 300 IN TXT  \"heritage=external-dns,external-dns/owner=external-dns,external-dns/resource=crd/default/test-domain-a\" [] api.example.local 300 IN A  192.168.1.20 [] example.local 3600 IN SOA  ns1.example.local. hostmaster.example.local. 2026030501 10800 3600 604800 3600 [] example.local 3600 IN NS  ns1.example.local []]"
time="2026-03-05T19:22:03Z" level=debug msg="dedupSource: collecting endpoints and removing duplicates"
time="2026-03-05T19:22:03Z" level=debug msg="multiSource: collecting endpoints from 3 child sources and removing duplicates"
time="2026-03-05T19:22:03Z" level=debug msg="No endpoints could be generated from 'service/powerdns/powerdns'"
time="2026-03-05T19:22:03Z" level=debug msg="No endpoints could be generated from 'service/default/kubernetes'"
time="2026-03-05T19:22:03Z" level=debug msg="No endpoints could be generated from 'service/external-dns/external-dns'"
time="2026-03-05T19:22:03Z" level=debug msg="No endpoints could be generated from 'service/ingress-nginx/ingress-nginx-controller'"
time="2026-03-05T19:22:03Z" level=debug msg="No endpoints could be generated from 'service/ingress-nginx/ingress-nginx-controller-admission'"
time="2026-03-05T19:22:03Z" level=debug msg="No endpoints could be generated from 'service/kube-system/kube-dns'"
time="2026-03-05T19:22:03Z" level=debug msg="ignoring record app.other.local that does not match domain filter"
time="2026-03-05T19:22:03Z" level=debug msg="ignoring record api.other.local that does not match domain filter"
time="2026-03-05T19:22:03Z" level=info msg="All records are already up to date"
time="2026-03-05T19:22:13Z" level=debug msg="Records fetched:\n[a-app.example.local 300 IN TXT  \"heritage=external-dns,external-dns/owner=external-dns,external-dns/resource=crd/default/test-domain-a\" [] app.example.local 300 IN A  192.168.1.10 [] a-api.example.local 300 IN TXT  \"heritage=external-dns,external-dns/owner=external-dns,external-dns/resource=crd/default/test-domain-a\" [] api.example.local 300 IN A  192.168.1.20 [] example.local 3600 IN SOA  ns1.example.local. hostmaster.example.local. 2026030501 10800 3600 604800 3600 [] example.local 3600 IN NS  ns1.example.local []]"
time="2026-03-05T19:22:13Z" level=debug msg="dedupSource: collecting endpoints and removing duplicates"
time="2026-03-05T19:22:13Z" level=debug msg="multiSource: collecting endpoints from 3 child sources and removing duplicates"
time="2026-03-05T19:22:13Z" level=debug msg="No endpoints could be generated from 'service/default/kubernetes'"
time="2026-03-05T19:22:13Z" level=debug msg="No endpoints could be generated from 'service/external-dns/external-dns'"
time="2026-03-05T19:22:13Z" level=debug msg="No endpoints could be generated from 'service/ingress-nginx/ingress-nginx-controller'"
time="2026-03-05T19:22:13Z" level=debug msg="No endpoints could be generated from 'service/ingress-nginx/ingress-nginx-controller-admission'"
time="2026-03-05T19:22:13Z" level=debug msg="No endpoints could be generated from 'service/kube-system/kube-dns'"
time="2026-03-05T19:22:13Z" level=debug msg="No endpoints could be generated from 'service/powerdns/powerdns'"
time="2026-03-05T19:22:13Z" level=debug msg="ignoring record app.other.local that does not match domain filter"
time="2026-03-05T19:22:13Z" level=debug msg="ignoring record api.other.local that does not match domain filter"
time="2026-03-05T19:22:13Z" level=info msg="All records are already up to date"
time="2026-03-05T19:22:24Z" level=debug msg="Records fetched:\n[a-app.example.local 300 IN TXT  \"heritage=external-dns,external-dns/owner=external-dns,external-dns/resource=crd/default/test-domain-a\" [] app.example.local 300 IN A  192.168.1.10 [] a-api.example.local 300 IN TXT  \"heritage=external-dns,external-dns/owner=external-dns,external-dns/resource=crd/default/test-domain-a\" [] api.example.local 300 IN A  192.168.1.20 [] example.local 3600 IN SOA  ns1.example.local. hostmaster.example.local. 2026030501 10800 3600 604800 3600 [] example.local 3600 IN NS  ns1.example.local []]"
time="2026-03-05T19:22:24Z" level=debug msg="dedupSource: collecting endpoints and removing duplicates"
time="2026-03-05T19:22:24Z" level=debug msg="multiSource: collecting endpoints from 3 child sources and removing duplicates"
time="2026-03-05T19:22:24Z" level=debug msg="No endpoints could be generated from 'service/kube-system/kube-dns'"
time="2026-03-05T19:22:24Z" level=debug msg="No endpoints could be generated from 'service/powerdns/powerdns'"
time="2026-03-05T19:22:24Z" level=debug msg="No endpoints could be generated from 'service/default/kubernetes'"
time="2026-03-05T19:22:24Z" level=debug msg="No endpoints could be generated from 'service/external-dns/external-dns'"
time="2026-03-05T19:22:24Z" level=debug msg="No endpoints could be generated from 'service/ingress-nginx/ingress-nginx-controller'"
time="2026-03-05T19:22:24Z" level=debug msg="No endpoints could be generated from 'service/ingress-nginx/ingress-nginx-controller-admission'"
time="2026-03-05T19:22:24Z" level=debug msg="ignoring record app.other.local that does not match domain filter"
time="2026-03-05T19:22:24Z" level=debug msg="ignoring record api.other.local that does not match domain filter"
time="2026-03-05T19:22:24Z" level=info msg="All records are already up to date"

[INFO] === Querying PowerDNS API for records ===
Forwarding from 127.0.0.1:8081 -> 8081
Forwarding from [::1]:8081 -> 8081

[INFO] --- Forward zone: example.local (filtered — expect A + TXT records) ---
Handling connection for 8081
{
    "account": "",
    "api_rectify": false,
    "catalog": "",
    "dnssec": false,
    "edited_serial": 2026030502,
    "id": "example.local.",
    "kind": "Native",
    "last_check": 0,
    "master_tsig_key_ids": [],
    "masters": [],
    "name": "example.local.",
    "notified_serial": 0,
    "nsec3narrow": false,
    "nsec3param": "",
    "rrsets": [
        {
            "comments": [],
            "name": "a-app.example.local.",
            "records": [
                {
                    "content": "\"heritage=external-dns,external-dns/owner=external-dns,external-dns/resource=crd/default/test-domain-a\"",
                    "disabled": false
                }
            ],
            "ttl": 300,
            "type": "TXT"
        },
        {
            "comments": [],
            "name": "app.example.local.",
            "records": [
                {
                    "content": "192.168.1.10",
                    "disabled": false
                }
            ],
            "ttl": 300,
            "type": "A"
        },
        {
            "comments": [],
            "name": "a-api.example.local.",
            "records": [
                {
                    "content": "\"heritage=external-dns,external-dns/owner=external-dns,external-dns/resource=crd/default/test-domain-a\"",
                    "disabled": false
                }
            ],
            "ttl": 300,
            "type": "TXT"
        },
        {
            "comments": [],
            "name": "api.example.local.",
            "records": [
                {
                    "content": "192.168.1.20",
                    "disabled": false
                }
            ],
            "ttl": 300,
            "type": "A"
        },
        {
            "comments": [],
            "name": "example.local.",
            "records": [
                {
                    "content": "ns1.example.local. hostmaster.example.local. 2026030501 10800 3600 604800 3600",
                    "disabled": false
                }
            ],
            "ttl": 3600,
            "type": "SOA"
        },
        {
            "comments": [],
            "name": "example.local.",
            "records": [
                {
                    "content": "ns1.example.local.",
                    "disabled": false
                }
            ],
            "ttl": 3600,
            "type": "NS"
        }
    ],
    "serial": 2026030501,
    "slave_tsig_key_ids": [],
    "soa_edit": "",
    "soa_edit_api": "DEFAULT",
    "url": "/api/v1/servers/localhost/zones/example.local."
}

[INFO] --- Forward zone: other.local (NOT filtered — expect NO extra records) ---
Handling connection for 8081
{
    "account": "",
    "api_rectify": false,
    "catalog": "",
    "dnssec": false,
    "edited_serial": 2026030501,
    "id": "other.local.",
    "kind": "Native",
    "last_check": 0,
    "master_tsig_key_ids": [],
    "masters": [],
    "name": "other.local.",
    "notified_serial": 0,
    "nsec3narrow": false,
    "nsec3param": "",
    "rrsets": [
        {
            "comments": [],
            "name": "other.local.",
            "records": [
                {
                    "content": "ns1.example.local. hostmaster.example.local. 0 10800 3600 604800 3600",
                    "disabled": false
                }
            ],
            "ttl": 3600,
            "type": "SOA"
        },
        {
            "comments": [],
            "name": "other.local.",
            "records": [
                {
                    "content": "ns1.other.local.",
                    "disabled": false
                }
            ],
            "ttl": 3600,
            "type": "NS"
        }
    ],
    "serial": 0,
    "slave_tsig_key_ids": [],
    "soa_edit": "",
    "soa_edit_api": "DEFAULT",
    "url": "/api/v1/servers/localhost/zones/other.local."
}

[INFO] =============================================
[INFO]  PR #6234 Test Results: GetDomainFilter
[INFO] =============================================

[PASS] app.example.local A record exists in zone example.local
[PASS] api.example.local A record exists in zone example.local
[PASS] app.other.local A record correctly absent from zone other.local
[PASS] api.other.local A record correctly absent from zone other.local

[INFO] =============================================
[INFO]  Results: 4 passed, 0 failed
[INFO] =============================================
[INFO] All tests passed!

@ivankatliarchuk
Copy link
Member

How to view your fixtures without downloading them ;-). Sry I do not trust random scripts on internet, and have no sandbox to hand.

I'll try to review over weekend once again.

@clwluvw
Copy link
Author

clwluvw commented Mar 6, 2026

@ivankatliarchuk - Please take a look at https://pastes.io/usrbinenv-52981

@clwluvw
Copy link
Author

clwluvw commented Mar 6, 2026

Copy link
Member

@ivankatliarchuk ivankatliarchuk left a comment

Choose a reason for hiding this comment

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

/approve

@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: ivankatliarchuk

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Mar 6, 2026
@ivankatliarchuk
Copy link
Member

I did no know that powerdns could run in kind/minikube. In regards e2e for powerdns it is possible. But most of the provider do require infrastructure and resources. If you want, 100% code coverage, then tutorial similar to #6048 and you could create e2e framework for powerdns, but this will require project owners review.

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

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. provider Issues or PRs related to a provider size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants