diff --git a/pkg/provider/kubernetes/ingress/kubernetes.go b/pkg/provider/kubernetes/ingress/kubernetes.go index ae2acf4cb..903a28839 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes.go +++ b/pkg/provider/kubernetes/ingress/kubernetes.go @@ -35,17 +35,25 @@ const ( // Provider holds configurations of the provider. type Provider struct { provider.BaseProvider `mapstructure:",squash" export:"true"` - Endpoint string `description:"Kubernetes server endpoint (required for external cluster client)"` - Token string `description:"Kubernetes bearer token (not needed for in-cluster client)"` - CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)"` - DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers" export:"true"` - EnablePassTLSCert bool `description:"Kubernetes enable Pass TLS Client Certs" export:"true"` // Deprecated - Namespaces k8s.Namespaces `description:"Kubernetes namespaces" export:"true"` - LabelSelector string `description:"Kubernetes Ingress label selector to use" export:"true"` - IngressClass string `description:"Value of kubernetes.io/ingress.class annotation to watch for" export:"true"` + Endpoint string `description:"Kubernetes server endpoint (required for external cluster client)"` + Token string `description:"Kubernetes bearer token (not needed for in-cluster client)"` + CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)"` + DisablePassHostHeaders bool `description:"Kubernetes disable PassHost Headers" export:"true"` + EnablePassTLSCert bool `description:"Kubernetes enable Pass TLS Client Certs" export:"true"` // Deprecated + Namespaces k8s.Namespaces `description:"Kubernetes namespaces" export:"true"` + LabelSelector string `description:"Kubernetes Ingress label selector to use" export:"true"` + IngressClass string `description:"Value of kubernetes.io/ingress.class annotation to watch for" export:"true"` + IngressEndpoint *EndpointIngress `description:"Kubernetes Ingress Endpoint"` lastConfiguration safe.Safe } +// EndpointIngress holds the endpoint information for the Kubernetes provider +type EndpointIngress struct { + IP string `description:"IP used for Kubernetes Ingress endpoints"` + Hostname string `description:"Hostname used for Kubernetes Ingress endpoints"` + PublishedService string `description:"Published Kubernetes Service to copy status from"` +} + func (p *Provider) newK8sClient(ctx context.Context, ingressLabelSelector string) (*clientWrapper, error) { ingLabelSel, err := labels.Parse(ingressLabelSelector) if err != nil { @@ -333,6 +341,10 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl conf.HTTP.Services[serviceName] = service } + err := p.updateIngressStatus(ingress, client) + if err != nil { + log.FromContext(ctx).Errorf("Error while updating ingress status: %v", err) + } } } @@ -429,3 +441,41 @@ func getCertificateBlocks(secret *corev1.Secret, namespace, secretName string) ( return cert, key, nil } + +func (p *Provider) updateIngressStatus(i *v1beta1.Ingress, k8sClient Client) error { + // Only process if an EndpointIngress has been configured + if p.IngressEndpoint == nil { + return nil + } + + if len(p.IngressEndpoint.PublishedService) == 0 { + if len(p.IngressEndpoint.IP) == 0 && len(p.IngressEndpoint.Hostname) == 0 { + return errors.New("publishedService or ip or hostname must be defined") + } + + return k8sClient.UpdateIngressStatus(i.Namespace, i.Name, p.IngressEndpoint.IP, p.IngressEndpoint.Hostname) + } + + serviceInfo := strings.Split(p.IngressEndpoint.PublishedService, "/") + if len(serviceInfo) != 2 { + return fmt.Errorf("invalid publishedService format (expected 'namespace/service' format): %s", p.IngressEndpoint.PublishedService) + } + serviceNamespace, serviceName := serviceInfo[0], serviceInfo[1] + + service, exists, err := k8sClient.GetService(serviceNamespace, serviceName) + if err != nil { + return fmt.Errorf("cannot get service %s, received error: %s", p.IngressEndpoint.PublishedService, err) + } + + if exists && service.Status.LoadBalancer.Ingress == nil { + // service exists, but has no Load Balancer status + log.Debugf("Skipping updating Ingress %s/%s due to service %s having no status set", i.Namespace, i.Name, p.IngressEndpoint.PublishedService) + return nil + } + + if !exists { + return fmt.Errorf("missing service: %s", p.IngressEndpoint.PublishedService) + } + + return k8sClient.UpdateIngressStatus(i.Namespace, i.Name, service.Status.LoadBalancer.Ingress[0].IP, service.Status.LoadBalancer.Ingress[0].Hostname) +}