Skip to content

refactor(controller): reduce cyclomatic complexity of buildProvider#6133

Closed
AndrewCharlesHay wants to merge 10 commits intokubernetes-sigs:masterfrom
AndrewCharlesHay:feat/reduce-complexity-build-provider
Closed

refactor(controller): reduce cyclomatic complexity of buildProvider#6133
AndrewCharlesHay wants to merge 10 commits intokubernetes-sigs:masterfrom
AndrewCharlesHay:feat/reduce-complexity-build-provider

Conversation

@AndrewCharlesHay
Copy link
Copy Markdown
Contributor

@AndrewCharlesHay AndrewCharlesHay commented Jan 20, 2026

What does it do ?

This PR refactors the buildProvider function in controller/execute.go to use a map-based lookup (providerFactories) instead of a large switch statement for initializing DNS providers. This changes the provider instantiation logic from a long series of conditional branches to a direct map lookup. The new complexity of this function is 5.

Motivation

The buildProvider function had a high cyclomatic complexity score (37), making it harder to maintain and test. By moving the provider initialization logic into a map of factory functions, we significantly reduce the complexity of the main execution flow and improve code readability.

More

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

@k8s-ci-robot k8s-ci-robot added controller Issues or PRs related to the controller size/L Denotes a PR that changes 100-499 lines, ignoring generated files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Jan 20, 2026
@coveralls
Copy link
Copy Markdown

coveralls commented Jan 20, 2026

Pull Request Test Coverage Report for Build 21227118902

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • 367 unchanged lines in 18 files lost coverage.
  • Overall coverage increased (+0.5%) to 79.528%

Files with Coverage Reduction New Missed Lines %
fake.go 1 96.23%
annotations/processors.go 2 97.26%
node.go 9 90.24%
controller.go 9 92.56%
crd.go 10 66.45%
gen/docs/sources/main.go 13 81.7%
openshift_route.go 15 84.96%
ingress.go 16 88.48%
execute.go 17 83.75%
istio_gateway.go 18 87.72%
Totals Coverage Status
Change from base Build 21092582142: 0.5%
Covered Lines: 16160
Relevant Lines: 20320

💛 - Coveralls

Copy link
Copy Markdown
Contributor

@vflaux vflaux left a comment

Choose a reason for hiding this comment

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

That’s something I’ve had in mind for a while too, and I’m in favor of this change.

We could then move each providerFactory function to its own package in follow‑up PRs.

Comment thread controller/execute_test.go Outdated
Comment thread controller/execute.go
@vflaux
Copy link
Copy Markdown
Contributor

vflaux commented Jan 20, 2026

/ok-to-test

@k8s-ci-robot k8s-ci-robot added the ok-to-test Indicates a non-member PR verified by an org member that is safe to test. label Jan 20, 2026
AndrewCharlesHay and others added 2 commits January 20, 2026 15:11
Co-authored-by: vflaux <38909103+vflaux@users.noreply.github.com>
@k8s-ci-robot k8s-ci-robot added the github_actions Pull requests that update GitHub Actions code label Jan 20, 2026
Comment thread controller/execute.go Outdated
return p, nil
}

type providerFactory func(context.Context, *externaldns.Config, *endpoint.DomainFilter) (provider.Provider, error)
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.

The benefit is not just in reducting complexity, but standartisation and refactoring. Wdyt to try something like below, this will require some provider standartisation.

type providerFactory map[string]Provider

new providerFactory(string, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider,error) {
 ... filters here, as ideally, all providers should support shared filters....
  return {
   "alibabacloud": alibabacloud.NewAlibabaCloudProvider(...),
   "azure": azure.NewAzureProvider(....)
   }
}

IF providerFactory is just temporary, we may not event need the struct, hard to say

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.

Good suggestion. I've implemented the providerFactories map as you proposed.

steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Set up Go
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 this go is fo?

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.

t.Context() was added in Go 1.24. The e2e tests were failing because the Go Version with Ubuntu latest was too old

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.

Maybe I'm too tired ;-). This e2e script does not require any go installed https://github.com/kubernetes-sigs/external-dns/blob/master/scripts/e2e-test.sh. Could you point where go is required for e2e tests?

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.

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.

Ko is a binary that is donwloaded with curl, it does not require go. Unless installed with go install github.com/google/ko@latest command. Could you share where it was failing?

Comment thread controller/execute.go Outdated
go handleSigterm(cancel)

sCfg := source.NewSourceConfig(cfg)
// TODO: Move source construction to the source package
Copy link
Copy Markdown
Member

@ivankatliarchuk ivankatliarchuk Jan 21, 2026

Choose a reason for hiding this comment

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

Not sure if this is feasable. This could introduce cyclic dependency.

Moved provider factories to a map in provider_factories.go to address PR kubernetes-sigs#6133 feedback.
@k8s-ci-robot
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please ask for approval from ivankatliarchuk. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found 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 size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Jan 21, 2026
@k8s-ci-robot k8s-ci-robot added the scripts Issues or PRs related to internal scripts label Jan 21, 2026
@ivankatliarchuk
Copy link
Copy Markdown
Member

ivankatliarchuk commented Jan 22, 2026

Not sure, why not to simply move it to provider package? But without this map[string]Function pattern, it looks like an additional complexity for not clear gain. Added here a bit more explanation https://github.com/kubernetes-sigs/external-dns/pull/6133/changes#r2716434342


type providerFactory func(context.Context, *externaldns.Config, *endpoint.DomainFilter) (provider.Provider, error)

var providerFactories = map[string]providerFactory{
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.

Using a map[string]func(...) like this is not inherently "bad Go", but some developers, including self, consider it a form of hidden dependency / service‑locator style factory, which is often indicatory that the code require a redesign.

is effectively:

  • A registry keyed by string identifiers.
  • Returning an interface (provider.Provider) from factory functions.
  • Selected at runtime by name rather than by explicit dependency injection.

Typical criticisms is that that apply directly to a map[string]providerFactory

  • Dependencies are discovered only at runtime (string key), not via explicit constructor parameters, which hurts static discoverability and refactoring.

Very much on the edge, as this is not necessary required

var providerFactories = map[string]providerFactory{........logic......}

It's affectively to bypass init(). So it has same issues, as if it was initilized at the init() phase. Sometimes there are no other way, like 3rd library demands it, but in our case, we not forced to do it that way.

I would encourage using common, familiar patterns and avoiding sophisticated machinery when a simpler construct works. This global providerFactories map is an extra layer of indirection that may not buy much over a simple switch. It hard to see what is actually wired where, at least for me.

@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jan 29, 2026
@k8s-ci-robot
Copy link
Copy Markdown
Contributor

PR needs rebase.

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.

@ivankatliarchuk
Copy link
Copy Markdown
Member

ivankatliarchuk commented Mar 16, 2026

Apologies. This was solved slightly different

/close

@k8s-ci-robot
Copy link
Copy Markdown
Contributor

@ivankatliarchuk: Closed this PR.

Details

In response to this:

Apologies. This was solved slightly different

/close

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.

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

Labels

cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. controller Issues or PRs related to the controller github_actions Pull requests that update GitHub Actions code needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. scripts Issues or PRs related to internal scripts size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants