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

Enable Ingress Status updates #3324

Merged
merged 7 commits into from May 18, 2018
Merged

Enable Ingress Status updates #3324

merged 7 commits into from May 18, 2018

Conversation

dtomcej
Copy link
Contributor

@dtomcej dtomcej commented May 14, 2018

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

  • Added/updated tests
  • Added/updated documentation

Additional Notes

This configuration is entirely optional, and will only apply if one of the 3 configuration settings are enabled.

@@ -166,6 +167,22 @@ func (c *clientImpl) GetIngresses() []*extensionsv1beta1.Ingress {
return result
}

//UpdateIngressStatus updates an Ingress with a provided status.
Copy link
Member

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

@@ -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)
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fun police ;)

@@ -129,6 +137,9 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
if err != nil {
return err
}
if err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

Seems not needed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Missed on move

@@ -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
Copy link
Member

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

@@ -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 != "" {
Copy link
Member

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

Copy link
Contributor Author

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!

} 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)
Copy link
Member

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.
Copy link
Member

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
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

All three are optional!

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
Copy link
Member

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

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
Copy link
Member

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
Copy link
Member

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.

Copy link
Member

@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.

LGTM

@ldez ldez added the kind/enhancement a new or improved feature. label May 16, 2018

err = p.updateIngressStatus(i, k8sClient)
if err != nil {
log.Printf("cannot update Ingress %s/%s due to error: %s", i.Namespace, i.Name, err)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

should be errorf

Copy link
Member

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

Copy link
Contributor

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?

Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Member

@ldez ldez May 18, 2018

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

Copy link
Member

@mmatur mmatur left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@m3co-code m3co-code left a 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.
Copy link
Contributor

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?

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 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.
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 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.
Copy link
Contributor

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?

@@ -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)
Copy link
Contributor

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.
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 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?

Copy link
Member

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.

Copy link
Contributor

@m3co-code m3co-code May 18, 2018

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.


err = p.updateIngressStatus(i, k8sClient)
if err != nil {
log.Printf("cannot update Ingress %s/%s due to error: %s", i.Namespace, i.Name, err)
Copy link
Contributor

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?


err = p.updateIngressStatus(i, k8sClient)
if err != nil {
log.Printf("cannot update Ingress %s/%s due to error: %s", i.Namespace, i.Name, err)
Copy link
Contributor

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.

}

if p.IngressEndpoint.PublishedService == "" {
return k8sClient.UpdateIngressStatus(i.Namespace, i.Name, p.IngressEndpoint.IP, p.IngressEndpoint.Hostname)
Copy link
Contributor

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?

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 is is expected that there would only be one of IP or Hostname ... perhaps we should check that they are not both empty?

Copy link
Member

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"
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 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


err = p.updateIngressStatus(i, k8sClient)
if err != nil {
log.Printf("cannot update Ingress %s/%s due to error: %s", i.Namespace, i.Name, err)
Copy link
Contributor

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.

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.

Design is very good, code is Clean

LGTM

Copy link
Contributor

@m3co-code m3co-code left a 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 👏

dtomcej and others added 7 commits May 18, 2018 11:48
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
@piclemx
Copy link

piclemx commented May 24, 2018

when this will be merged?

@ldez
Copy link
Member

ldez commented May 24, 2018

It's already merged.

@anandsinghkunwar
Copy link

Will this be cherry-picked for the older versions?

@timoreimann
Copy link
Contributor

@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.

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

9 participants