k8s ErrorPage middleware now uses k8s service
This commit is contained in:
parent
34be181706
commit
fb8edd86d5
7 changed files with 130 additions and 8 deletions
|
@ -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.
|
||||
|
|
15
pkg/provider/kubernetes/crd/fixtures/with_error_page.yml
Normal file
15
pkg/provider/kubernetes/crd/fixtures/with_error_page.yml
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue