Skip to content
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

Support filtering ingresses on ingressClassName as well as deprecated kubernetes.io/ingress.class annotation #2054

Merged

Conversation

dsalisbury
Copy link
Contributor

@dsalisbury dsalisbury commented Apr 18, 2021

Description

Adds the ability to filter ingresses based on their IngressClassName attribute. There's an --ingress-class-filter argument that's now exposed through the CLI and some additional filtering in source/ingress.go

Fixes #1975 #1792

Checklist

  • Unit tests updated
  • End user documentation updated

@k8s-ci-robot k8s-ci-robot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Apr 18, 2021
@k8s-ci-robot k8s-ci-robot added the size/M Denotes a PR that changes 30-99 lines, ignoring generated files. label Apr 18, 2021
@dsalisbury
Copy link
Contributor Author

dsalisbury commented Apr 18, 2021

So one thing I have outstanding is detecting the current default IngressClass (as per https://kubernetes.io/docs/concepts/services-networking/ingress/#default-ingress-class)

Here's my thinking:

  1. in the setup of source/ingress.go, load the IngressClasses, find any with the special annotation, and store this away
  2. the default class is used for any ingresses which are found and don't have a class explicitly
  3. if more than 1 default ingress class is found then assume no default ingress?

    Caution: If you have more than one IngressClass marked as the default for your cluster, the admission controller prevents creating new Ingress objects that don't have an ingressClassName specified. You can resolve this by ensuring that at most 1 IngressClass is marked as default in your cluster.

  4. We probably need to watch for changes to the ingress classes and reconcile if/when the default changes
  5. In an RBAC cluster, I think this will require some extra permissions so doc updates and things will be needed

~Any thoughts on this @Raffo? ~

@seanmalloy
Copy link
Member

/kind feature

@k8s-ci-robot k8s-ci-robot added the kind/feature Categorizes issue or PR as related to a new feature. label May 12, 2021
@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 14, 2021
@FalconerTC
Copy link
Contributor

Any updates here? Would really like to see this feature get through

@wagnst
Copy link

wagnst commented Sep 30, 2021

@dsalisbury can you fix the merge conflicts, maybe then someone will review?

@dsalisbury
Copy link
Contributor Author

Update on things:

  1. detecting default ingress class isn't necessary; if there's a default and a new ingress is created with no class name set, the admission controller sets it explicitly.
  2. code is updated and rebased on latest master

we don't want to get incompatible restrictions by letting someone set
"--ingress-class" and --annotation-filter="kubernetes.io/ingress.class"
at the same time
@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Oct 2, 2021
@dsalisbury
Copy link
Contributor Author

dsalisbury commented Oct 2, 2021

I think this is ready for review!

I've done some manual verification locally with a kind cluster connected to pdns in the following scenarios:

  • two IngressClass defined (with one as default) and one Ingress of each class; checked with zero, one, and two --ingress-class args
  • Two IngressClass and neither is default, with an Ingress of each class
  • use of --ingress-class to match deprecated annotation-based ingress class specification
  • use of mixed ingress.class annotation and ingressClassName

All scenarios worked correctly as far as I could tell

@dsalisbury dsalisbury marked this pull request as ready for review October 2, 2021 06:40
@k8s-ci-robot k8s-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Oct 2, 2021
for _, nameFilter := range sc.ingressClassNames {
if ingress.Spec.IngressClassName != nil && nameFilter == *ingress.Spec.IngressClassName {
matched = true
} else if matchLabelSelector(selector, ingress.Annotations) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this block does the filtering with fallback

@szuecs
Copy link
Contributor

szuecs commented Jan 5, 2023

Maybe to add more context. I joined a couple of months ago to help shrinking the backlog.

@ryan-dyer-sp TBH I think you and other commenters show not a very nice community communication style. Naturally I would give more priority to people who are more friendly, because I don't want to spend time with unfriendly people.

You can just scroll the comments and see for example (others are not better and I don't want to pick one particular user here):

How hard could it be to review a PR this simple?

Examples like this make me re-thinking if I want to spend time maintaining this project, because it's not respectful IMO.

Please also understand that as a maintainer I am free to review any PR that I feel is interesting for the project. Maybe I looked at it already, but think it's wrong, but did not care because I don't use the feature and think the feature should be rather abandoned (just hypothetical not really true, because I am aware that people run multiple instances of external-dns).

@KarstenSiemer
Copy link
Contributor

@szuecs thanks for your work inside the OSS world!
Lets not get disheartened. This is a wonderful project and it will keep being one.
I think OSS is all about respect from everyone and to everyone.

And someone used this project, tested it and has found something to improve and even submitted a PR, all for $0 and without even being a maintainer. Few people spend time on this problem and just felt left alone.
I'd say we should leave the personal stuff out of this and keep concentrated on the actual problem.

If there are any open tasks to get this feature to a production stage, I am also willing to implement.

@szuecs
Copy link
Contributor

szuecs commented Jan 6, 2023

@KarstenSiemer thanks and I fully agree let's make it. Yesterday I added my questions, it's not that I have string opinions on them, but maybe it would be great to have some mixed config possible for migration purposes.
In other controller I first check ingressClass and then annotation, so ingressClass has precedence, so why do we check exclusive ingressClass vs. annotation?

Comment on lines +261 to +263
To do this with ExternalDNS you can use the `--ingress-class` to specifically tie an instance of ExternalDNS to
an instance of a ingress controller. Let's assume you have two ingress controllers `nginx-internal` and `nginx-external`
then you can start two ExternalDNS providers one with `--ingress-class=nginx-internal` and one with `--ingress-class=nginx-external`.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
To do this with ExternalDNS you can use the `--ingress-class` to specifically tie an instance of ExternalDNS to
an instance of a ingress controller. Let's assume you have two ingress controllers `nginx-internal` and `nginx-external`
then you can start two ExternalDNS providers one with `--ingress-class=nginx-internal` and one with `--ingress-class=nginx-external`.
To do this with ExternalDNS you can use the `--ingress-class` flag to specifically tie an instance of ExternalDNS to
an instance of a ingress controller. Let's assume you have two ingress controllers, `nginx-internal` and `nginx-external`.
You can then start two ExternalDNS providers, one with `--ingress-class=nginx-internal` and one with `--ingress-class=nginx-external`.


Beware when using multiple sources, e.g. `--source=service --source=ingress`, `--annotation-filter` will filter every given source objects.
If you need to filter only one specific source you have to run a separated external dns service containing only the wanted `--source` and `--annotation-filter`.
The `--ingress-class` argument will check both the `ingressClassName` field as well as the deprecated `kubernetes.io/ingress.class` annotation.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
The `--ingress-class` argument will check both the `ingressClassName` field as well as the deprecated `kubernetes.io/ingress.class` annotation.
The `--ingress-class` flag will check both the `ingressClassName` field and the deprecated `kubernetes.io/ingress.class` annotation.

Copy link

Choose a reason for hiding this comment

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

“argument” is preferable. Golang calls command line arguments “flags” whether the type is a boolean or not, but the rest of the world of IT typically uses “flag” just for booleans (maybe tristates).


**Note:** Filtering based on annotation means that the external-dns controller will receive all resources of that kind and then filter on the client-side.
Note: the `--ingress-class` argument cannot be used at the same time as a `kubernetes.io/ingress.class` annotation filter; if you do this an error will be raised.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Note: the `--ingress-class` argument cannot be used at the same time as a `kubernetes.io/ingress.class` annotation filter; if you do this an error will be raised.
Note: the `--ingress-class` flag cannot be used at the same time as a `kubernetes.io/ingress.class` annotation filter; if you do this an error will be raised.

Copy link

Choose a reason for hiding this comment

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

// class
func (sc *ingressSource) filterByIngressClass(ingresses []*networkv1.Ingress) ([]*networkv1.Ingress, error) {
// if no class filter is specified then there's nothing to do
if sc.ingressClassNames == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if sc.ingressClassNames == nil {
if len(sc.ingressClassNames) == 0 {


requirements, _ := selector.Requirements()
for _, requirement := range requirements {
if requirement.Key() == "kubernetes.io/ingress.class" {
Copy link
Contributor

Choose a reason for hiding this comment

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

filterByIngressClass() does the fallback logic when considering whether an Ingress matches the requirement set by the --ingress-class flag. For that reason, passing both an --ingress-class flag and a kubernetes.io/ingress.class annotation filter doesn't make sense: it would exclude those ingresses that don't match the annotation filter. Thus, this check is a reasonable validation against the admin passing nonsensical flag combinations.

annotations: map[string]string{
"kubernetes.io/ingress.class": "dmz",
},
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggest test case with annotation dmz and ingressClassName internal. Should not be in expected.

Possibly also annotation internal ingressClassName dmz. Should be in expected.

Suggest test case with neither annotation nor ingressClassName. Should not be in expected.

@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: dsalisbury
Once this PR has been reviewed and has the lgtm label, please ask for approval from njuettner by writing /assign @njuettner in a comment. For more information see the Kubernetes Code Review Process.

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 /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Copy link

@sftim sftim left a comment

Choose a reason for hiding this comment

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

Does this need to change too? It mentions kubernetes.io/ingress.class

Additionally, you may leverage [cert-manager](https://github.com/jetstack/cert-manager) to automatically issue SSL certificates from [Let's Encrypt](https://letsencrypt.org/). To do that, request a certificate in public service definition:

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    certmanager.k8s.io/acme-challenge-type: "dns01"
    certmanager.k8s.io/acme-dns01-provider: "route53"
    certmanager.k8s.io/cluster-issuer: "letsencrypt-production"
    kubernetes.io/ingress.class: "external-ingress"
    kubernetes.io/tls-acme: "true"

Copy link

@sftim sftim left a comment

Choose a reason for hiding this comment

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

Does the FAQ question I have a Service/Ingress but it's ignored by ExternalDNS. Why?“ need a new answer?

return ingresses, nil
}

classNameReq, err := labels.NewRequirement("kubernetes.io/ingress.class", selection.In, sc.ingressClassNames)
Copy link

Choose a reason for hiding this comment

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

Labels? Is that right? The deprecated kubernetes.io/ingress.class annotation is an annotation, not a label.

However, beware when using annotation filters with multiple sources, e.g. `--source=service --source=ingress`, since `--annotation-filter` will filter every given source objects.
If you need to use annotation filters against a specific source you have to run a separated external dns service containing only the wanted `--source` and `--annotation-filter`.

**Note:** Filtering based on annotation or ingress class name means that the external-dns controller will receive all resources of that kind and then filter on the client-side.
In larger clusters with many resources which change frequently this can cause performance issues. If only some resources need to be managed by an instance
of external-dns then label filtering can be used instead of annotation filtering. This means that only those resources which match the selector specified
Copy link

Choose a reason for hiding this comment

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

Suggested change
of external-dns then label filtering can be used instead of annotation filtering. This means that only those resources which match the selector specified
of external-dns then label filtering can be used instead of ingress class filtering (or legacy annotation filtering). This means that only those resources which match the selector specified

?

@hdiass
Copy link

hdiass commented Apr 5, 2023

whats preventing this PR to be merged ?

@mateuszdrab
Copy link

whats preventing this PR to be merged ?

Not sure, this PR is notoriously ignored by the maintainers.

I made a workaround that works and fits into my design well so this feature is not needed so critically but would still be useful to address a few edge cases.

@hdiass
Copy link

hdiass commented Apr 5, 2023

how did you worked this around without annotations ?

@mateuszdrab
Copy link

how did you worked this around without annotations ?

It was with annotations, but not the ingress.class one. Since my external facing records are cnames, I set external DNS to only act when the target annotation is in place. Still, it would be more convenient to just scope it to one ingress class.

@hdiass
Copy link

hdiass commented Apr 5, 2023

Yeah i did the same, but is annoying when you already have that meta in the ingressClassName

@szuecs
Copy link
Contributor

szuecs commented Apr 5, 2023

There are more than a couple of suggestions to change by 2 different members and as a maintainer I wait such that these are addressed.
@mateuszdrab please be careful what you write, because it's wrong and you create just hate to us, which is not fine.

@mateuszdrab
Copy link

mateuszdrab commented Apr 5, 2023

There are more than a couple of suggestions to change by 2 different members and as a maintainer I wait such that these are addressed.
@mateuszdrab please be careful what you write, because it's wrong and you create just hate to us, which is not fine.

I understand and I feel bad for making it sound like this. It is just how I feel having seen someone put the effort into making this PR and I'd have thought the maintainers are here to drive progression and not just approve/deny changes.

A lot of conversation is about ethics and little is done to progress with merging this. I don't like political discussion so I generally don't comment when unnecessary. It sort of appears to me that the maintainers get defensive as soon as people start asking for attention on this PR.

I see new releases come out without this feature and it's been a long time since this was opened - it personally doesn't affect me as I only run this in a home lab. From a perspective of other users, it could be more significant.

However, I have a right to express my opinion, and my opinion is that bugs/issues should be addressed before new features and deprecation of the ingress annotation is quite a fundamental change, a breaking change for some people who might be heavily dependent on external DNS and filtering by ingress classes. Kubernetes will not allow you to have both ingressclassname and the annotation in place and therefore the lack of support here could be potentially a blocker in migration to higher versions forcing some to stay on unsupported versions. We're now on 1.27 almost and this was deprecated on 1.18 so almost 10 releases ago...

If I was the person who made a PR and couldn't get it merged for 2 years despite it being realtively small in size I'd just give up and it might be why the OP isn't responding.

@szuecs
Copy link
Contributor

szuecs commented Apr 5, 2023

As I wrote the pr was reviewed by two kubernetes contributors and there was no change and you did just bike shedding @mateuszdrab which is not ok.

@mateuszdrab
Copy link

mateuszdrab commented Apr 5, 2023

I understand that the PR has been reviewed but let me highlight one fact that it appears that @dsalisbury has not made any contributions on GitHub for the past 6 months.
Community members are asking for progress and it's all stalled because one person is not engaging. We don't know if he will come back here, but we do know this PR is needed - what can be done to make progress on this?

Can we make suggestions to his code and you maintainers can merge those in? (did he allow maintainer edits?)
Do we need someone else to take ownership of this code and drive towards merging?
Do we leave it to stale out and die only for someone else to open an issue on it again - this does not sound like a good option, does it?

@szuecs
Copy link
Contributor

szuecs commented Apr 6, 2023

@mateuszdrab take ownership if you want to have the pr merged and commit the changes then we will review the pr as people show here interest.

@dsalisbury
Copy link
Contributor Author

I understand that the PR has been reviewed but let me highlight one fact that it appears that @dsalisbury has not made any contributions on GitHub for the past 6 months.
Community members are asking for progress and it's all stalled because one person is not engaging. We don't know if he will come back here, but we do know this PR is needed - what can be done to make progress on this?

Yep hi, that's me. Apologies for lack of engagement, but I don't have time or capacity to see this through any more. Anyone who wants is welcome to take the branch and run with it themselves or just throw it away and start over.

@k8s-ci-robot
Copy link
Contributor

PR needs rebase.

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/test-infra repository.

@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Apr 25, 2023
@alefray
Copy link
Contributor

alefray commented May 4, 2023

Yep hi, that's me. Apologies for lack of engagement, but I don't have time or capacity to see this through any more. Anyone who wants is welcome to take the branch and run with it themselves or just throw it away and start over.

Thanks to @dsalisbury and to all the reviewers for their work 🙏

I've commit the changes asked in #3589

I hope we can finish this 😉 !

@k8s-ci-robot k8s-ci-robot merged commit f76382a into kubernetes-sigs:master May 9, 2023
@johngmyers johngmyers mentioned this pull request Jun 7, 2023
2 tasks
@johngmyers johngmyers mentioned this pull request Sep 13, 2023
2 tasks
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. kind/feature Categorizes issue or PR as related to a new feature. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. 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.

Allow to configure a ingressClassName filter