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

Allow custom value for kubernetes.io/ingress.class annotation #2222

Merged
merged 17 commits into from
Feb 1, 2018
Merged

Allow custom value for kubernetes.io/ingress.class annotation #2222

merged 17 commits into from
Feb 1, 2018

Conversation

yuvipanda
Copy link
Contributor

@yuvipanda yuvipanda commented Oct 7, 2017

Description

Allow configuring the values of the kubernetes.io/ingress.class annotation
on ingress objects that traefik will process. This is useful when you have
multiple traefik servers in one kubernetes cluster, and you want tight control
over which Ingresses are processed by which servers.

Fixes #2221

@ldez
Copy link
Contributor

ldez commented Oct 7, 2017

@containous/kubernetes WDYT? (Design review)

@timoreimann
Copy link
Contributor

Technically, I'd tentatively approve the design.

Whether we should accept the PR though is still subject to an agreement that the existing ways to distinguish separate Traefik deployments is really insufficient. I have commented the related feature request issue for that purpose. I suggest we wait for a resolution prior to moving forward with an actual review.

@timoreimann
Copy link
Contributor

timoreimann commented Oct 8, 2017

As explained on the related issue, the desirable feature is to have a subset of Traefik controllers ignore certain Ingresses by default unless intent to process is signaled explicitly (i.e., opt-in instead of opt-out). Traefik label selectors don't fulfill this requirement since the default behavior of a missing selector is to grab Ingress objects.

Acknowledging the feature request, I'm uncertain if we should reuse kubernetes.io/ingress.class for the desired purpose. As far as I know, other Ingress implementations use it to rule out a particular class of controllers only (as the name implies), not for fine-grained discriminations within a single controller. My worry is that allowing arbitrary values may collide with potential other Ingress controllers in the future, possibly becoming a source of confusion.

Maybe a better approach could be to introduce a new annotation -- I'm thinking of something including the term sub-class. One thing I'm not sure about though is if it would live under the ingress.kubernetes.io/ or a custom traefik.*/ prefix.

@aledbf any thoughts on how the feature should be best implemented in terms of annotations? Could you maybe share some information on how new ingress.kubernetes.io/ annotations get added? As far as I know, there's no standardization process available yet. Is it as simple as one Ingress controller implementation taking a lead and others following (with the lead usually being Nginx)?

Pulling in @dtomcej and @errm for additional feedback.

@yuvipanda
Copy link
Contributor Author

I'll note that the nginx controller does allow you to change this (in the --ingress-class commandline parameter), which is where I got this idea from.

@timoreimann
Copy link
Contributor

@yuvipanda thanks, I missed that! (Was only looking at the annotation documentation.)

Let's see what the folks I pinged for feedback think.

@aledbf
Copy link

aledbf commented Oct 8, 2017

@timoreimann the annotation kubernetes.io/ingress.class is the "proper" way to filter which ingress rules should be processed or not.

any thoughts on how the feature should be best implemented in terms of annotations?

The use of annotations is being reviewed and possible Custom Resources is going to be the way to handle the ingress configuration. I am not sure when this will discussed in #sig-network

Edit: the ingress.class annotation is present since the first Ingress iteration https://github.com/aledbf/ingress/blob/master/examples/PREREQUISITES.md#ingress-class

@errm
Copy link
Contributor

errm commented Oct 9, 2017

I think that I am fine with this... I think it can cause some confusion, but since the default will behave no differently from the current situation I think this is ok.

I would want a clear paragraph explaining the options in the documentation before we merge this this though...

@timoreimann
Copy link
Contributor

@aledbf @errm thanks for your feedback, appreciated.

I think we're good to move forward with the actual review then.

Copy link
Contributor

@ldez ldez left a comment

Choose a reason for hiding this comment

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

Thanks 👍

ingressClassRequired = p.IngressClass
}

switch ingressClass {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you simplify like that (I know this code already exists before your changes):

return ingressClass == "" || ingressClass == ingressClassRequired

Copy link
Contributor

Choose a reason for hiding this comment

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

Appreciated! I think it was me who originally made this more complicated than necessary. :/

@yuvipanda
Copy link
Contributor Author

w00t, thanks! Should I figure out where to add the docs and just add it now or wait for a code review ok first?

@ldez
Copy link
Contributor

ldez commented Oct 9, 2017

You can write the documentation right now. This can facilitate the code review.

Copy link
Contributor

@timoreimann timoreimann left a comment

Choose a reason for hiding this comment

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

Two general remarks:

  1. We should extend TestIngressAnnotations by a new test case that sets p.IngressClass to a custom value.
  2. We recently merged Document ways to partition Ingresses in the k8s guide. #2223 into the v1.4 which added a section on the class annotation here. I believe it would be the ideal place to extend our documentation. @ldez, any chance we can merge v1.4 into master soonish to make it available for this PR? If not, I'm okay with omitting more detailed docs out now and amending them once we have completed the merge.

@@ -51,6 +51,15 @@ See also [Kubernetes user guide](/user-guide/kubernetes).
#
# labelselector = "A and not B"

# Value of `kubernetes.io/ingress.class` annotation that identify Ingress objects to be processed.
Copy link
Contributor

Choose a reason for hiding this comment

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

Grammar nit-pick: identify -> identifies.

ingressClassRequired := "traefik"
if p.IngressClass != "" {
ingressClassRequired = p.IngressClass
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can skip the if block if we choose a default value of "" (empty string) for IngressClass.

# will also be processed.
#
# Optional
# Default: traefik (process all Ingresses)
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can make things easier by choosing the empty string as default. Specifically:

  1. We don't specifically need to set IngressClass to traefik in tests.
  2. We can simplify shouldProcessIngress (see below).

@@ -135,6 +136,20 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
return nil
}

func (p *Provider) shouldProcessIngress(ingressClass string) bool {
ingressClassRequired := "traefik"
Copy link
Contributor

@timoreimann timoreimann Oct 9, 2017

Choose a reason for hiding this comment

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

We could make a constant out of this like

const ingressClassRequired = "traefik"

to indicate that this is not a string to be mutated.

That said, I don't think we need the variable/constant anyway if we decide to use the empty string as default value for p.IngressClass.

Copy link

@aledbf aledbf Oct 9, 2017

Choose a reason for hiding this comment

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

@timoreimann you should not this but use a flag with a default ("traefik"). This allows the user to deploy multiple controllers with different class annotations to expose different ingress

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah sorry, late night logic flaw on my end. @aledbf is right, we need the default of "traefik".

@yuvipanda please ignore my comments in this regard.

Copy link
Contributor

@timoreimann timoreimann Oct 9, 2017

Choose a reason for hiding this comment

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

So in summary, keeping the default of p.IngressClass to traefik should still allow us to simplify the method implementation like this:

func (p *Provider) shouldProcessIngress(ingressClass string) bool {
        return ingressClass == "" || ingressClass == p.IngressClass
}

I think this should work, though only tests will bring certainty...

@yuvipanda
Copy link
Contributor Author

Thanks for the review everyone! I'm caught up with other work this week, but will try to find some time within the next few days to address them!

Copy link
Contributor

@dtomcej dtomcej left a comment

Choose a reason for hiding this comment

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

Ok Sorry for being late to the party on this one.

I have a few concerns:

It appears that we are allowing custom ingress classes, but we do not do any sanity checking on them. What happens if we set the ingress class to "nginx"? Is that something we want to allow/support?

I totally get the nested controller/namespace issue that you are encountering. However, I would propose the following:

Require the traefik ingress class to be traefik or prefixed with traefik-. This would be a sanity check, and would avoid crossover with other ingress controllers etc.

Your jupyter app could create ingresses with the traefik-<namespace> or traefik-<UUID> format, and would be fine, and would function the way you expect them to, while still being visibly traefik related :) .

I am hesitant to approve fully custom classes.

@timoreimann
Copy link
Contributor

I like the prefix idea too, even though it means diverging from the Nginx equivalent option somewhat.

@yuvipanda
Copy link
Contributor Author

That looks good to me too! I'm making the required changes now.

@yuvipanda
Copy link
Contributor Author

So looks like the things I need to do still are:

  1. Figure out where defaults are set, and set default for IngressClass to 'traefik'. The only place I could find is in cmd/traefik/configuration.go. Is that the right place? I've already modified it...
  2. Figure out where validation logic for this config goes, and make sure that this value is either 'traefik' or starts with 'traefik-'
  3. Write an additional test.

Does that sound right?

I'm still sortof new to go, so any pointers for where to find (1) and (2) (or links to similar PRs / functionality in other providers) will be highly appreciated!

@yuvipanda
Copy link
Contributor Author

(I also did a quick rebase to prevent my branch from getting too stale)

@timoreimann
Copy link
Contributor

@yuvipanda

  1. I think you need to add a line here.
  2. We need to validate the annotation for every Ingress object we're inspecting. Presumably, the check should happen inside shouldProcessIngress and log in case we use an illegal prefix.
  3. Yes. :-)

Side note: I think we should allow the prefix traefik, not only traefik-, supporting cases where users want to have something like traefik1, traefik2, traefik42, etc. The dash gives extra structure, which in my opinion should be up to the user to decide for or against.

@yuvipanda
Copy link
Contributor Author

@timoreimann thank you for your quick response!

I've already done (1) early on, so I've just gotten rid of the custom check in shouldProcessIngress. I wasn't sure if (1) sets the defaults everywhere (including tests) or just when running the binary.

For (2), can you help me understand why we validating that there instead of wherever it is the config.toml file is being parsed? Wouldn't that help us error out earlier? Or do we need to do this for each ingress because traefik's configuration is so dynamic?

I'll work on tests next!

Copy link
Contributor

@dtomcej dtomcej left a comment

Choose a reason for hiding this comment

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

LGTM
:shipit:

Copy link
Contributor

@timoreimann timoreimann left a comment

Choose a reason for hiding this comment

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

LGTM -- thanks a ton to both @yuvipanda for the original effort and @nmengin for finishing off! 👏

I can take care of extending the guide in a follow up PR.

@beyond-code-github
Copy link

I think we should also extend the Kubernetes guide at https://github.com/containous/traefik/blob/d426126a9294af8df041e7e55cbd61b8b899a4e1/docs/user-guide/kubernetes.md#between-tr%C3%A6fik-and-other-ingress-controller-implementations.

Did this ever get completed? I just read this thread with interest but it's not clear how to make use of the new feature that this PR added.

Can I just add IngressClass = "traefik-xxx" to my .toml file?

@timoreimann
Copy link
Contributor

@beyond-code-github I could have sworn that I pushed a follow-up PR to update the docs. Looks like I never did though!

Thanks for the hint, will fix that soon. In the meantime, the comment to the ingressClass option should answer your question:

# Value of `kubernetes.io/ingress.class` annotation that identifies Ingress objects to be processed.
# If the parameter is non-empty, only Ingresses containing an annotation with the same value are processed.
# Otherwise, Ingresses missing the annotation, having an empty value, or the value `traefik` are processed.
#
# Note : `ingressClass` option must begin with the "traefik" prefix.
#
# Optional
# Default: empty
#
# ingressClass = "traefik-internal"

So the answer to your question is: yes, traefik-xxx will work because it comes with the necessary traefik prefix.

@beyond-code-github
Copy link

Thanks for the swift response @timoreimann !

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

Successfully merging this pull request may close these issues.

9 participants