-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
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
Conversation
We create a rule using the `PathPrefixStrip` to trim out the bit in the rewrite rule.
@mlaccetti do you possibly know if the /cc @dtomcej @errm; also @emilevauge who I remember was involved in a Slack discussion on this topic fairly recently. |
Related to #1722 |
@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. |
Should we write this as a project-wide rule type? and just have ingresses implement it on annotation? |
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. |
provider/kubernetes/kubernetes.go
Outdated
default: | ||
templateObjects.Frontends[r.Host+pa.Path].Routes[pa.Path] = types.Route{ | ||
Rule: ruleType + ":" + pa.Path, | ||
} |
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.
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,
}
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.
Simplified
ObjectMeta: v1.ObjectMeta{ | ||
Namespace: "testing", | ||
Annotations: map[string]string{ | ||
"kubernetes.io/ingress.class": "traefik", |
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.
We don't need the Ingress class for the test. Please remove it.
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.
Removed
provider/kubernetes/kubernetes.go
Outdated
default: | ||
templateObjects.Frontends[r.Host+pa.Path].Routes[pa.Path] = types.Route{ | ||
Rule: ruleType + ":" + pa.Path, | ||
} |
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.
Could we move the logic pertaining to setting the route type to a dedicated function to ease readability?
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.
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)) |
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.
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()
.
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.
Cut over to asserting on the structs.
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.) |
@timoreimann design review before review please. 😉 |
@ldez true. however, the only open point left seems to be the one from @dtomcej's comment, which should be unrelated to my comments. |
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. |
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:
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? |
@timoreimann I've made the changes, though I have no idea if I made it any better. :D |
@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? |
@timoreimann Yes, that discussion should be a separate Proposal for handling these sort of annotation updates. More will come. |
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.
Moar comments. :-)
provider/kubernetes/kubernetes.go
Outdated
@@ -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 { |
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.
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.
provider/kubernetes/kubernetes.go
Outdated
@@ -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) { |
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.
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.
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.
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.") |
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.
I think we can drop the extra message. testify will tell us by default that the expected and actual values mismatch.
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.
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.
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.
Thanks a lot! LGTM! 👏
@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. |
@mlaccetti updating the branch should fix things. could you do that please? |
@mlaccetti please rebase instead of merging. |
@timoreimann Sorry, hitting the Hopefully somebody can merge the PR, now! |
@mlaccetti no worries. PR looks good now! By the way: rebasing is as simple as doing the following two git operations:
That'll unwrap all local commits from your PR branch temporarily; fast-forward the branch to the latest of |
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.
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...
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.
Clean implementation.
LGTM
@mlaccetti could you please consider adding an example for |
@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! :) |
ping @mlaccetti :–) |
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.