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

kubernetes ingress rewrite-target implementation #1723

Merged
merged 19 commits into from
Jul 7, 2017
Merged

kubernetes ingress rewrite-target implementation #1723

merged 19 commits into from
Jul 7, 2017

Conversation

mlaccetti
Copy link
Contributor

Monitor the kubernetes ingress for the ingress.kubernetes.io/rewrite-target annotation, and if seen, configure the frontend to strip off the provided path, and replace with the one provided in the annotation.

We create a rule using the `PathPrefixStrip` to trim out the bit in the rewrite rule.
@timoreimann
Copy link
Contributor

@mlaccetti do you possibly know if the ingress.kubernetes.io/rewrite-target annotation is somewhat of a standard, or rather something that comes from a specific controller implementation (presumably Nginx)?

/cc @dtomcej @errm; also @emilevauge who I remember was involved in a Slack discussion on this topic fairly recently.

@ldez ldez changed the title kubernetes ingress rewrite-target implementation - for #1722 kubernetes ingress rewrite-target implementation Jun 8, 2017
@ldez
Copy link
Member

ldez commented Jun 8, 2017

Related to #1722

@mlaccetti
Copy link
Contributor Author

mlaccetti commented Jun 8, 2017

@timoreimann The spec doesn't have any annotations listed, but I'm finding them cropping up in places, and it seemed like low-hanging fruit.

It seems that folks have started using the nginx ingress as a defacto standard, rather than actually chasing down the spec. I'm going to chase that one down to figure out what the long term plan is.

Edit: I take it back, they are in the docs! https://github.com/kubernetes/ingress/blob/master/docs/annotations.md - Seems they are actually standard, now.

@ldez
Copy link
Member

ldez commented Jun 8, 2017

@dtomcej
Copy link
Contributor

dtomcej commented Jun 9, 2017

Should we write this as a project-wide rule type? and just have ingresses implement it on annotation?

@timoreimann
Copy link
Contributor

The annotations look more like de-facto standards to me. Regardless, I'm okay with supporting it.

I'd probably scope it to Kubernetes for now. If there's demand for spreading, we can have further PRs.

default:
templateObjects.Frontends[r.Host+pa.Path].Routes[pa.Path] = types.Route{
Rule: ruleType + ":" + pa.Path,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

To me it looks like we could simplify this block. How about:

rule := ruleType + ":" + pa.path

if rewriteTarget := i.Annotations[annotationKubernetesRewriteTarget]; rewriteTarget != "" {
  rule = ruleTypeReplacePath + ":" + rewriteTarget
}

templateObjects.Frontends[r.Host+pa.Path].Routes[pa.Path] = types.Route{
  Rule: rule,
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Simplified

ObjectMeta: v1.ObjectMeta{
Namespace: "testing",
Annotations: map[string]string{
"kubernetes.io/ingress.class": "traefik",
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't need the Ingress class for the test. Please remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

default:
templateObjects.Frontends[r.Host+pa.Path].Routes[pa.Path] = types.Route{
Rule: ruleType + ":" + pa.Path,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we move the logic pertaining to setting the route type to a dedicated function to ease readability?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved it into a function.

},
}

actualJSON, _ := json.Marshal(actual)
expectedJSON, _ := json.Marshal(expected)

if !reflect.DeepEqual(actual, expected) {
t.Fatalf("expected %+v, got %+v", string(expectedJSON), string(actualJSON))
t.Fatalf("expected %+v \n got %+v", string(expectedJSON), string(actualJSON))
Copy link
Contributor

Choose a reason for hiding this comment

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

We should stop comparing JSON representations as they can hide differences in very subtle ways. For instance, an empty slice and a nil one would not be DeepEqual but look the same in the JSON output.

Either compare on the actual and expected structs, or use assert.Equal().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cut over to asserting on the structs.

@timoreimann
Copy link
Contributor

Related discussion on the Kubernetes mailing thread I just ran into: https://groups.google.com/d/topic/kubernetes-users/jkaCCykAwXA/discussion

(tl;dr: The current naming isn't too well-defined.)

@ldez
Copy link
Member

ldez commented Jun 9, 2017

@timoreimann design review before review please. 😉

@timoreimann
Copy link
Contributor

@ldez true. however, the only open point left seems to be the one from @dtomcej's comment, which should be unrelated to my comments.

@dtomcej
Copy link
Contributor

dtomcej commented Jun 9, 2017

My concern is that every other rule we have is project wide. It would seem weird IMO to have rule types lumped in that ONLY work on kubernetes ingresses.

@timoreimann
Copy link
Contributor

timoreimann commented Jun 11, 2017

I agree that it'd look weird on first sight. IMHO though, we might lose more than we can win by spreading the rule for a few reasons:

  • We have already established a common, consistent naming scheme across all providers (mostly surrounding the notion of paths) which the new name doesn't fit in well (which obviously is because it comes from an external source). I can imagine non-Kubernetes users being irritated by this, and our only answer would be "it's because of Kubernetes".
  • Even if we made it a global rule, we likely can't even make it consistent within Traefik: The ingress.kubernetes.io/ prefix would only exist with Kubernetes while all the other providers would come without it. I'm concerned this may lead to more confusion.
  • The Kubernetes provider already diverges from the other ones: you only specify the rule type but not the entire rule, and AddPath doesn't exist yet. The reason from my view is that Ingress already provides a specification that we have to cope with. I'm not entirely sure if we can solve the problem in a consistent manner. I'm inclined to say that Kubernetes is "special" and that we shouldn't necessarily try to squeeze it into our existing scheme.
  • The way I interpreted the mailing thread I posted above is that the Kubernetes maintainers themselves aren't overly happy with the naming. To me there's still room for the naming to change -- at least, it's nothing like an official standard for now. So I'd rather not have it spread to other providers until there's reasonable certainty.
  • If we were to do this for one Kubernetes rule, we'd have to do it for future ones coming as well. (The list is there already.) This would multiply my consistency concerns.

Regardless of how we decide on this point, do we agree that this PR is okay to proceed? AFAICS, there's nothing in it that would affect any future decision to the extent we're concerned about. Maybe we can then postpone the bigger decision to a dedicated issue?

@mlaccetti
Copy link
Contributor Author

@timoreimann I've made the changes, though I have no idea if I made it any better. :D

@timoreimann
Copy link
Contributor

timoreimann commented Jun 13, 2017

@mlaccetti seems better on first sight to me. :-)

@dtomcej WDYT, should we treat the should-or-should-not-be-a-project-wide-rule-type question separate from the functionality this PR provides?

@dtomcej
Copy link
Contributor

dtomcej commented Jun 13, 2017

@timoreimann Yes, that discussion should be a separate Proposal for handling these sort of annotation updates. More will come.

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.

Moar comments. :-)

@@ -294,6 +291,25 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
return &templateObjects, nil
}

func setPath(pa v1beta1.HTTPIngressPath, i *v1beta1.Ingress, templateObjects types.Configuration, r v1beta1.IngressRule) {
if len(pa.Path) > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we reduce the set of input parameters to the bare minimum needed for the implementation? For instance, it looks like we don't need the entire Ingress struct, just the annotations.

@@ -294,6 +291,25 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
return &templateObjects, nil
}

func setPath(pa v1beta1.HTTPIngressPath, i *v1beta1.Ingress, templateObjects types.Configuration, r v1beta1.IngressRule) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Another approach to implement this function might be to just have it return the computed rule and let the caller assign it to the configuration. That way, we don't need to pass in the configurations.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I twiddled the implementation from setPath to getRuleForPath, and, assuming a rule is found, apply it. That should address the two issues you highlighted.

if !reflect.DeepEqual(actual, expected) {
t.Fatalf("expected %+v, got %+v", string(expectedJSON), string(actualJSON))
}
assert.Equal(t, expected, actual, "The rewritten data did not match the expected.")
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 drop the extra message. testify will tell us by default that the expected and actual values mismatch.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed the extra message - legacy of JUnit where you don't always get a "nice" error message, and spitting out some words helped make more sense of things.

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.

Thanks a lot! LGTM! 👏

@mlaccetti
Copy link
Contributor Author

@timoreimann The build is failing, but looks like a glitch with Semaphore CI - any chance you can kick it? Otherwise, I'll try to find some code to change, then revert, just to force it.

@timoreimann
Copy link
Contributor

@mlaccetti updating the branch should fix things. could you do that please?

@timoreimann
Copy link
Contributor

@mlaccetti please rebase instead of merging.

@mlaccetti
Copy link
Contributor Author

@timoreimann Sorry, hitting the update branch button in GitHub, seems to do a merge instead of a rebase. (And, to be honest, I haven't learned how to do a rebase, either.)

Hopefully somebody can merge the PR, now!

@timoreimann
Copy link
Contributor

@mlaccetti no worries. PR looks good now!

By the way: rebasing is as simple as doing the following two git operations:

  1. git fetch origin master (or whatever your local repository name for containous/traefik is).
  2. (in your local PR branch:) git rebase origin/master

That'll unwrap all local commits from your PR branch temporarily; fast-forward the branch to the latest of upstream/master; and reapply your local commits again. Naturally, you may have to resolve conflicts just like with a regular merge (though in a rebase, the roles of "mine" and "theirs" are swapped).

@timoreimann
Copy link
Contributor

@dtomcej @errm and others: PTAL. :-)

Copy link
Contributor

@errm errm left a comment

Choose a reason for hiding this comment

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

LGTM

The change seems clean to me, and the tests look good.

I am honestly not that bothered by divergence from the other providers, if we are doing something that provides compatibility with a "defacto" standard from the kubernetes ecosystem...

I was thinking about some other options WRT this that we could explore in the future... e.g. providing an alternate kubernetes provider that used a CustomResourceDefinition closely aligned to traefik...

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.

Clean implementation.

LGTM

:shipit:

@ldez ldez merged commit 41dd124 into traefik:master Jul 7, 2017
@ldez ldez added this to the 1.4 milestone Jul 7, 2017
@kachkaev
Copy link
Contributor

@mlaccetti could you please consider adding an example for rewrite-target to https://github.com/containous/traefik/blob/master/docs/configuration/backends/kubernetes.md? Do you think it could solve the trailing slash issue? #563 (comment), #563 (comment)?

@mlaccetti
Copy link
Contributor Author

@kachkaev I'll see if I can find some time this week to add the example in - could arguably solve the problem - just have two ingresses! :)

@kachkaev
Copy link
Contributor

ping @mlaccetti :–)

@ldez
Copy link
Member

ldez commented Jan 8, 2018

@kachkaev look at #2676

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.

None yet

6 participants