k8s ErrorPage middleware now uses k8s service

This commit is contained in:
Julien Salleyron 2019-09-10 17:24:03 +02:00 committed by Traefiker Bot
parent 34be181706
commit fb8edd86d5
7 changed files with 130 additions and 8 deletions

View file

@ -29,8 +29,10 @@ spec:
errors:
status:
- 500-599
service: serviceError
query: /{status}.html
service:
name: whoami
port: 80
```
```json tab="Marathon"
@ -95,6 +97,9 @@ The status code ranges are inclusive (`500-599` will trigger with every code bet
The service that will serve the new requested error page.
!!! Note
In kubernetes, you need to reference a kubernetes service instead of a traefik service.
### `query`
The URL for the error page (hosted by `service`). You can use `{status}` in the query, that will be replaced by the received status code.

View file

@ -0,0 +1,15 @@
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: errorpage
namespace: default
spec:
errors:
status:
- "404"
- "500"
query: query
service:
name: whoami
port: 80

View file

@ -170,6 +170,18 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
continue
}
errorPage, errorPageService, err := createErrorPageMiddleware(client, middleware.Namespace, middleware.Spec.Errors)
if err != nil {
log.FromContext(ctxMid).Errorf("Error while reading error page middleware: %v", err)
continue
}
if errorPage != nil && errorPageService != nil {
serviceName := id + "-errorpage-service"
errorPage.Service = serviceName
conf.HTTP.Services[serviceName] = errorPageService
}
conf.HTTP.Middlewares[id] = &dynamic.Middleware{
AddPrefix: middleware.Spec.AddPrefix,
StripPrefix: middleware.Spec.StripPrefix,
@ -179,7 +191,7 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
Chain: createChainMiddleware(ctxMid, middleware.Namespace, middleware.Spec.Chain),
IPWhiteList: middleware.Spec.IPWhiteList,
Headers: middleware.Spec.Headers,
Errors: middleware.Spec.Errors,
Errors: errorPage,
RateLimit: middleware.Spec.RateLimit,
RedirectRegex: middleware.Spec.RedirectRegex,
RedirectScheme: middleware.Spec.RedirectScheme,
@ -199,6 +211,24 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
return conf
}
func createErrorPageMiddleware(client Client, namespace string, errorPage *v1alpha1.ErrorPage) (*dynamic.ErrorPage, *dynamic.Service, error) {
if errorPage == nil {
return nil, nil, nil
}
errorPageMiddleware := &dynamic.ErrorPage{
Status: errorPage.Status,
Query: errorPage.Query,
}
balancerServerHTTP, err := createLoadBalancerServerHTTP(client, namespace, errorPage.Service)
if err != nil {
return nil, nil, err
}
return errorPageMiddleware, balancerServerHTTP, nil
}
func createForwardAuthMiddleware(k8sClient Client, namespace string, auth *v1alpha1.ForwardAuth) (*dynamic.ForwardAuth, error) {
if auth == nil {
return nil, nil

View file

@ -64,7 +64,7 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
serviceName := makeID(ingressRoute.Namespace, key)
for _, service := range route.Services {
balancerServerHTTP, err := createLoadBalancerServerHTTP(client, ingressRoute, service)
balancerServerHTTP, err := createLoadBalancerServerHTTP(client, ingressRoute.Namespace, service)
if err != nil {
logger.
WithField("serviceName", service.Name).
@ -151,8 +151,8 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
return conf
}
func createLoadBalancerServerHTTP(client Client, ingressRoute *v1alpha1.IngressRoute, service v1alpha1.Service) (*dynamic.Service, error) {
servers, err := loadServers(client, ingressRoute.Namespace, service)
func createLoadBalancerServerHTTP(client Client, namespace string, service v1alpha1.Service) (*dynamic.Service, error) {
servers, err := loadServers(client, namespace, service)
if err != nil {
return nil, err
}
@ -181,7 +181,7 @@ func loadServers(client Client, namespace string, svc v1alpha1.Service) ([]dynam
}
if !exists {
return nil, errors.New("service not found")
return nil, fmt.Errorf("service not found %s/%s", namespace, svc.Name)
}
var portSpec *corev1.ServicePort

View file

@ -1498,6 +1498,44 @@ func TestLoadIngressRoutes(t *testing.T) {
},
},
},
{
desc: "Simple Ingress Route, with error page middleware",
paths: []string{"services.yml", "with_error_page.yml"},
expected: &dynamic.Configuration{
TLS: &dynamic.TLSConfiguration{},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{
"default/errorpage": {
Errors: &dynamic.ErrorPage{
Status: []string{"404", "500"},
Service: "default/errorpage-errorpage-service",
Query: "query",
},
},
},
Services: map[string]*dynamic.Service{
"default/errorpage-errorpage-service": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: true,
},
},
},
},
},
},
{
desc: "port selected by name (TODO)",
},
@ -1506,6 +1544,9 @@ func TestLoadIngressRoutes(t *testing.T) {
for _, test := range testCases {
test := test
if test.desc != "Simple Ingress Route, with error page middleware" {
continue
}
t.Run(test.desc, func(t *testing.T) {
t.Parallel()

View file

@ -28,7 +28,7 @@ type MiddlewareSpec struct {
Chain *Chain `json:"chain,omitempty"`
IPWhiteList *dynamic.IPWhiteList `json:"ipWhiteList,omitempty"`
Headers *dynamic.Headers `json:"headers,omitempty"`
Errors *dynamic.ErrorPage `json:"errors,omitempty"`
Errors *ErrorPage `json:"errors,omitempty"`
RateLimit *dynamic.RateLimit `json:"rateLimit,omitempty"`
RedirectRegex *dynamic.RedirectRegex `json:"redirectRegex,omitempty"`
RedirectScheme *dynamic.RedirectScheme `json:"redirectScheme,omitempty"`
@ -45,6 +45,15 @@ type MiddlewareSpec struct {
// +k8s:deepcopy-gen=true
// ErrorPage holds the custom error page configuration.
type ErrorPage struct {
Status []string `json:"status,omitempty"`
Service Service `json:"service,omitempty"`
Query string `json:"query,omitempty"`
}
// +k8s:deepcopy-gen=true
// Chain holds a chain of middlewares
type Chain struct {
Middlewares []MiddlewareRef `json:"middlewares,omitempty"`

View file

@ -124,6 +124,28 @@ func (in *DigestAuth) DeepCopy() *DigestAuth {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ErrorPage) DeepCopyInto(out *ErrorPage) {
*out = *in
if in.Status != nil {
in, out := &in.Status, &out.Status
*out = make([]string, len(*in))
copy(*out, *in)
}
in.Service.DeepCopyInto(&out.Service)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ErrorPage.
func (in *ErrorPage) DeepCopy() *ErrorPage {
if in == nil {
return nil
}
out := new(ErrorPage)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ForwardAuth) DeepCopyInto(out *ForwardAuth) {
*out = *in
@ -480,7 +502,7 @@ func (in *MiddlewareSpec) DeepCopyInto(out *MiddlewareSpec) {
}
if in.Errors != nil {
in, out := &in.Errors, &out.Errors
*out = new(dynamic.ErrorPage)
*out = new(ErrorPage)
(*in).DeepCopyInto(*out)
}
if in.RateLimit != nil {