-
Notifications
You must be signed in to change notification settings - Fork 2.8k
feat(aws): add warning for provider-specific properties without setIdentifier #5799
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
feat(aws): add warning for provider-specific properties without setIdentifier #5799
Conversation
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
Hi @u-kai. 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 Once the patch is verified, the new status will be reflected by the I understand the commands that are listed here. 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@u-kai Thanks for this PR to improve userXP.
The use case looks good to me 👍 .
For the implementation, it seems quite inefficient to re-browse all those fields.
Wdyt of adding this debug log inside this loop instead of creating a dedicated loop ? Would it work or did I miss something ?
Thank you for the suggestion! However, I agree about efficiency. |
That would be a good first step. I also noticed that => Wdyt of adding an intersect method ? Using an Hash for |
Thanks — does this implementation look correct? var providerSpecificRequiringSetIdentifier = []string{
providerSpecificWeight,
providerSpecificRegion,
providerSpecificFailover,
providerSpecificGeolocationContinentCode,
providerSpecificGeolocationCountryCode,
providerSpecificGeolocationSubdivisionCode,
providerSpecificGeoProximityLocationAWSRegion,
providerSpecificGeoProximityLocationBias,
providerSpecificGeoProximityLocationCoordinates,
providerSpecificGeoProximityLocationLocalZoneGroup,
providerSpecificMultiValueAnswer,
}
....
if setIdentifier == "" {
ignoredProperties := make([]string, 0, len(providerSpecificRequiringSetIdentifier))
tmpMap := make(map[string]struct{}, len(ep.ProviderSpecific))
for _, ps := range ep.ProviderSpecific {
tmpMap[ps.Name] = struct{}{}
}
for _, prop := range providerSpecificRequiringSetIdentifier {
if _, ok := tmpMap[prop]; ok {
ignoredProperties = append(ignoredProperties, prop)
}
}
if len(ignoredProperties) > 0 {
log.Warnf("Endpoint %s has provider-specific properties %v that require a setIdentifier, but none was set; ignoring these properties",
ep.DNSName, ignoredProperties)
}
} It should indeed be faster. However, I have a couple of concerns: We’re accessing the Given the numbers involved — So this is a readability vs. optimization trade-off. |
@u-kai you can check k8s.io/utils/set package. There is an |
@vflaux if setIdentifier == "" {
providerSpecificSet := make(set.Set[string], len(ep.ProviderSpecific))
for _, s := range ep.ProviderSpecific {
providerSpecificSet.Insert(s.Name)
}
ignoredProperties := providerSpecificRequiringSetIdentifier.Intersection(providerSpecificSet)
if len(ignoredProperties) > 0 {
pMsg := ignoredProperties.SortedList()
log.Warnf("Endpoint %s has provider-specific properties %v that require a setIdentifier, but none was set; ignoring these properties",
ep.DNSName, pMsg)
}
} |
No need to range over providerSpecificSet := set.New(ep.ProviderSpecific...) |
Thanks! Just to clarify: |
@u-kai You're right, my mistake. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I actually see here. This change reveals a design solution where at the moment
// ProviderSpecific holds configuration which is specific to individual DNS providers
type ProviderSpecific []ProviderSpecificProperty
func (e *Endpoint) GetProviderSpecificProperty(key string) (string, bool) {
for _, providerSpecific := range e.ProviderSpecific {
if providerSpecific.Name == key {
return providerSpecific.Value, true
}
}
return "", false
}
func (e *Endpoint) SetProviderSpecificProperty(key string, value string) {
for i, providerSpecific := range e.ProviderSpecific {
if providerSpecific.Name == key {
e.ProviderSpecific[i] = ProviderSpecificProperty{
Name: key,
Value: value,
}
return
}
}
e.ProviderSpecific = append(e.ProviderSpecific, ProviderSpecificProperty{Name: key, Value: value})
}
Throughout the codebase, whenever external-dns needs to retrieve a provider-specific property, it performs an iteration. In large environments, this could become a performance bottleneck.
Before adding a warning logg (not something super critical), we should most likely first consider the possible data structures that could better fit ProviderSpecific
use case.
|
||
setIdentifier := ep.SetIdentifier | ||
|
||
// Check if provider-specific values requiring setIdentifier are present but setIdentifier is empty |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably there are other design solutions. But could we have it at least a private method, as I see no point to increase complexity as it currently does?
You're right, the current slice-based design might not be the most efficient, and switching to a map could be a better fit here. |
I looked into it, and changing To avoid that, my plan is: Keep the CRD interface as-is ( Define a separate internal Endpoint type, where Add conversion helpers between the CRD type and the internal type. This way we can improve performance and readability without introducing a breaking change to users. Does that sound reasonable to you? |
Makes sense on paper))) |
If the other change (the O(1) logic) isn't approved, it doesn't make sense to add a warning message, as that would only increase complexity. So most likely this is going to be on hold |
Could you clarify a bit more on your first point? 🙇
I’d like to better understand why the warning message would lose its meaning or increase complexity if the O(1) logic isn’t approved. From my perspective, based on some quick benchmarks I ran, for our typical workload in external-dns (3–5 ProviderSpecific keys, ~30 membership checks in the AWS provider paths), a simple for loop over the slice is actually faster and simpler than building a set/map. So my current plan is to adjust this PR to keep the underlying representation as a slice and switch back to a straightforward loop, while still adding the warning log as originally intended. I’d appreciate a quick sanity check on this assumption. If this looks reasonable, I’d like to proceed in that direction. |
/hold |
@u-kai I'll try to explain.
So let's try to take some high level point of view: Is adding this kind of check for all resources loaded by ExternalDNS is an improvement on UserXP or overengineering ? (ie: adding more complexity to the code base) Normally, when a required parameter is not there, the API called should fail. In this specific case, The call to AWS API should fail, return an error and so the log would be displayed to the user. Like for all the other error cases with this provider. We can improve our documentation but like @ivankatliarchuk, I'm not sure we should complexify our code base because of this specific edge case. The AWS APIs are evolving and, maybe this case will return an error in a few months. But, maybe I missed something. Feel free to share your thoughts. |
Thanks for the detailed feedback.
I understand your concern, but just to clarify — this change only applies to the AWS provider, and only when setIdentifier is empty. You're right that it adds some complexity, but as the issue reporter mentioned, users currently get no warning at all when this happens, which can be confusing. I think this small check improves UX in such cases.
Yes, the AWS API already returns an error like below.
However, ExternalDNS currently only sets the providerSpecific property when setIdentifier is non-empty, which means the AWS API never gets a chance to validate the invalid case. As an alternative approach, in a future version, we could consider setting the property regardless of setIdentifier and let the AWS API error out. For now, I believe adding a warn-level log strikes a good balance between UX improvement and safety. |
What does it do ?
Add warning logs when AWS Route53 provider-specific routing policy properties (weight, region, failover, geolocation, geoproximity, multi-value) are specified without the required setIdentifier.
This helps users identify misconfigurations where their routing policies are silently ignored by Route53.
Motivation
Add warning logs when AWS Route53 provider-specific routing policy properties (weight, region, failover, geolocation, geoproximity, multi-value) are specified without the required setIdentifier.
This helps users identify misconfigurations where their routing policies are silently ignored by Route53.
Fixes #5775
When users configure AWS Route53 routing policies using ExternalDNS annotations like
external-dns.alpha.kubernetes.io/aws-weight: "200"
but forget to includeexternal-dns.alpha.kubernetes.io/set-identifier
, Route53 silently ignores the routing policy and creates a standard DNS record instead.This leads to confusion as users expect weighted/failover routing to be active but see no effect.
More