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
Enable Ingress Status updates #3324
Conversation
provider/kubernetes/client.go
Outdated
@@ -166,6 +167,22 @@ func (c *clientImpl) GetIngresses() []*extensionsv1beta1.Ingress { | |||
return result | |||
} | |||
|
|||
//UpdateIngressStatus updates an Ingress with a provided status. |
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 you add a space between //
and the message
provider/kubernetes/client.go
Outdated
@@ -166,6 +167,22 @@ func (c *clientImpl) GetIngresses() []*extensionsv1beta1.Ingress { | |||
return result | |||
} | |||
|
|||
//UpdateIngressStatus updates an Ingress with a provided status. | |||
func (c *clientImpl) UpdateIngressStatus(namespace, name, ip, hostname string) error { | |||
item, _, _ := c.factories[c.lookupNamespace(namespace)].Extensions().V1beta1().Ingresses().Informer().GetStore().GetByKey(namespace + "/" + name) |
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.
You need to manage the error.
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.
Fun police ;)
provider/kubernetes/kubernetes.go
Outdated
@@ -129,6 +137,9 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s | |||
if err != nil { | |||
return err | |||
} | |||
if err != nil { |
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.
Seems not needed
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.
Missed on move
provider/kubernetes/kubernetes.go
Outdated
@@ -155,6 +167,50 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s | |||
return nil | |||
} | |||
|
|||
func (p *Provider) updateIngressStatuses(k8sClient Client) error { | |||
//Only run these ingress updates if config is provided. This will reduce overhead |
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 you add a space between //
and the message
provider/kubernetes/kubernetes.go
Outdated
@@ -155,6 +167,50 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s | |||
return nil | |||
} | |||
|
|||
func (p *Provider) updateIngressStatuses(k8sClient Client) error { | |||
//Only run these ingress updates if config is provided. This will reduce overhead | |||
if p.IngressEndpoint.Hostname != "" || p.IngressEndpoint.IP != "" || p.IngressEndpoint.PublishedService != "" { |
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 you need to check if p.IngressEndpoint
is not nil
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.
Ah yes. It breaks spectacularly if p.IngressEndpoint
is nil!
provider/kubernetes/kubernetes.go
Outdated
} else { | ||
err := k8sClient.UpdateIngressStatus(i.Namespace, i.Name, p.IngressEndpoint.IP, p.IngressEndpoint.Hostname) | ||
if err != nil { | ||
log.Errorf("Cannot update Ingress %s/%s due to error: %s", i.Namespace, i.Name, err) |
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 the log can be transform to an error.
|
||
``` | ||
|
||
You can configure a static hostname or IP address that traefik will add to the status section of ingress objects that it manages. If you prefer, you can provide a service, which traefik will copy the status spec from. This will give more flexibility in cloud/dynamic environments. |
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.
One sentence, one line
# ip = 127.0.0.1 | ||
# | ||
# Optional | ||
# publishedservice = default/cheddar |
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.
all these values don't seem optional.
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.
All three are optional!
provider/kubernetes/kubernetes.go
Outdated
s := strings.Split(p.IngressEndpoint.PublishedService, "/") | ||
serviceNamespace, serviceName := s[0], s[1] | ||
svc, exists, _ := k8sClient.GetService(serviceNamespace, serviceName) | ||
//Set the status to the status of the fronting LB service |
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 you add a space between //
and the message
provider/kubernetes/kubernetes.go
Outdated
svc, exists, _ := k8sClient.GetService(serviceNamespace, serviceName) | ||
//Set the status to the status of the fronting LB service | ||
if exists && svc.Status.LoadBalancer.Ingress == nil { | ||
//service exists, but has no LoadBalancer status |
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 you add a space between //
and the message
# Enable IngressEndpoint configuration. This will allow Traefik to update the status section of ingress objects. | ||
# | ||
# Optional | ||
# Default: false |
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.
You could remove this line, because kubernetes.ingressendpoint
is not a boolean.
I know that related to CLI option.
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
provider/kubernetes/kubernetes.go
Outdated
|
||
err = p.updateIngressStatus(i, k8sClient) | ||
if err != nil { | ||
log.Printf("cannot update Ingress %s/%s due to error: %s", i.Namespace, i.Name, err) |
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.
should be errorf
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.
Not here, because we don't want to stop to browse all other ingress
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.
But why would log.Errorf
stop processing? If it's an error we should mark it that way in the logs, shouldn't we?
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.
You mix %s
and %v
for errors. I don't really mind as there is no logical difference in the output, but consistence is a thing and %s
should be faster as it doesn't have to do type assertions and therefore would stick with 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.
Logs should start with upper case.
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.
oh yes sorry I was thinking about fmt.Errorf
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
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.
@dtomcej I really like the code clarity and structure, nice job! Most of my comments are nit-picky as hell and they are basically only about consistency in how we write things.
I think that we should write some unit tests for the updateIngressStatus
and UpdateIngressStatus
method respectively, WDYT?
# [kubernetes.ingressEndpoint] | ||
# | ||
# One must be configured. | ||
# Publishedservice will override the hostname and ip settings if configured. |
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.
Shouldn't PublishedService be "camel cased" here?
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 ip should be written IP as it's usual in go.
@@ -105,14 +119,20 @@ A label selector can be defined to filter on specific Ingress objects only. | |||
|
|||
See [label-selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) for details. | |||
|
|||
### `ingressEndpoint` | |||
|
|||
You can configure a static hostname or IP address that traefik will add to the status section of ingress objects that it manages. |
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 the name traefik should be written as Traefik. At least we usually do this.
@@ -105,14 +119,20 @@ A label selector can be defined to filter on specific Ingress objects only. | |||
|
|||
See [label-selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) for details. | |||
|
|||
### `ingressEndpoint` | |||
|
|||
You can configure a static hostname or IP address that traefik will add to the status section of ingress objects that it manages. |
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.
You mix ingress and Ingress. I think for Kubernetes objects it would make sense to always write them in uppercase. WDYT?
provider/kubernetes/client.go
Outdated
@@ -166,6 +167,33 @@ func (c *clientImpl) GetIngresses() []*extensionsv1beta1.Ingress { | |||
return result | |||
} | |||
|
|||
// UpdateIngressStatus updates an Ingress with a provided status. | |||
func (c *clientImpl) UpdateIngressStatus(namespace, name, ip, hostname string) error { | |||
item, exists, err := c.factories[c.lookupNamespace(namespace)].Extensions().V1beta1().Ingresses().Informer().GetStore().GetByKey(namespace + "/" + name) |
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.
Should we extract the Ingress identifier (namespace + "/" + name) into a variable? You're rebuilding the same string multiple times in the method.
# | ||
# [kubernetes.ingressEndpoint] | ||
# | ||
# One must be configured. |
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 have to emphasize more that there are two different configuration possibilities. So either: hostname + IP OR the service name. Or do I understand this wrong?
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 the explanation is on the next line.
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 change it to At least one must be configured
? This + the next sentence describes everything well then.
provider/kubernetes/kubernetes.go
Outdated
|
||
err = p.updateIngressStatus(i, k8sClient) | ||
if err != nil { | ||
log.Printf("cannot update Ingress %s/%s due to error: %s", i.Namespace, i.Name, err) |
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.
But why would log.Errorf
stop processing? If it's an error we should mark it that way in the logs, shouldn't we?
provider/kubernetes/kubernetes.go
Outdated
|
||
err = p.updateIngressStatus(i, k8sClient) | ||
if err != nil { | ||
log.Printf("cannot update Ingress %s/%s due to error: %s", i.Namespace, i.Name, err) |
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.
You mix %s
and %v
for errors. I don't really mind as there is no logical difference in the output, but consistence is a thing and %s
should be faster as it doesn't have to do type assertions and therefore would stick with it.
provider/kubernetes/kubernetes.go
Outdated
} | ||
|
||
if p.IngressEndpoint.PublishedService == "" { | ||
return k8sClient.UpdateIngressStatus(i.Namespace, i.Name, p.IngressEndpoint.IP, p.IngressEndpoint.Hostname) |
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.
Should we maybe emit a warning when one of the fields is an empty string? Or is that 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 is is expected that there would only be one of IP or Hostname ... perhaps we should check that they are not both 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.
I added a check if the both are empty.
# | ||
# hostname = "localhost" | ||
# ip = "127.0.0.1" | ||
# publishedService = "default/cheddar" |
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 document that it's namespace/servicename? Or do you think this is clear to Kubernetes users? I didn't get it in the first moments. Maybe simply a different example would help like publishedService = namespaceName/serviceName
provider/kubernetes/kubernetes.go
Outdated
|
||
err = p.updateIngressStatus(i, k8sClient) | ||
if err != nil { | ||
log.Printf("cannot update Ingress %s/%s due to error: %s", i.Namespace, i.Name, err) |
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.
Logs should start with upper case.
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.
Design is very good, code is Clean
LGTM
f6ab2b2
to
ff83b6a
Compare
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 for walking with me through the nit-pick marshes! Great job guys 👏
add mock function test factory clean up kube go fix client usage create endpoint type add service status copy update docs gofmt golint code review fixes wtf spelling update docs review: simplify. fix test panic fix spacing
when this will be merged? |
It's already merged. |
Will this be cherry-picked for the older versions? |
@anandsinghkunwar we cherry-pick bug fixes only to minimize the risk of affecting otherwise stable releases adversely. Especially in this case, the code delta is quite big. Traefik 1.7 is on the horizon though, it shouldn't take much longer anymore until we ship. |
What does this PR do?
Allows Traefik to update the Status section of Ingress objects either via static IP/Hostname configuration, or via extracting the status section of a fronting service.
Motivation
Fixes #2173
Users requested that the status field be set so that DNS and other discovery tools would work properly.
More
Additional Notes
This configuration is entirely optional, and will only apply if one of the 3 configuration settings are enabled.