Skip to content

Commit

Permalink
Use errors middleware to redirect on idled workspace after 4 seconds
Browse files Browse the repository at this point in the history
Signed-off-by: David Kwon <[email protected]>
  • Loading branch information
dkwon17 committed Jun 9, 2022
1 parent 0bc1049 commit c956fe1
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 8 deletions.
20 changes: 20 additions & 0 deletions controllers/devworkspace/solver/che_routing.go
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,8 @@ func provisionMainWorkspaceRoute(cheCluster *chev2.CheCluster, routing *dwo.DevW
// authorize against kube-rbac-proxy in che-gateway. This will be needed for k8s native auth as well.
cfg.AddAuth(dwId, "http://127.0.0.1:8089?namespace="+dwNamespace)

add5XXErrorHandling(cfg, dwId)

// make '/healthz' path of main endpoints reachable from outside
routeForHealthzEndpoint(cfg, dwId, routing.Spec.Endpoints)

Expand All @@ -505,6 +507,24 @@ func provisionMainWorkspaceRoute(cheCluster *chev2.CheCluster, routing *dwo.DevW
}
}

func add5XXErrorHandling(cfg *gateway.TraefikConfig, dwId string) {

// revalidate cache to prevent case where redirect to dashboard after trying to restart an idled workspace
noCacheHeader := map[string]string{"cache-control": "no-store, max-age=0"}
cfg.AddResponseHeaders(dwId, noCacheHeader)

// dashboard service name must match Traefik dashboard service name
dashboardServiceName := defaults.GetCheFlavor() + "-dashboard"
cfg.AddErrors(dwId, "500-599", dashboardServiceName, "/")
cfg.HTTP.ServersTransports = map[string]*gateway.TraefikConfigServersTransport{}
cfg.HTTP.ServersTransports[dwId] = &gateway.TraefikConfigServersTransport{
ForwardingTimeouts: &gateway.TraefikConfigForwardingTimeouts{
DialTimeout: "4s",
},
}
cfg.HTTP.Services[dwId].LoadBalancer.ServersTransport = dwId
}

// makes '/healthz' path of main endpoints reachable from the outside
func routeForHealthzEndpoint(cfg *gateway.TraefikConfig, dwId string, endpoints map[string]dwo.EndpointList) {
for componentName, endpoints := range endpoints {
Expand Down
23 changes: 19 additions & 4 deletions controllers/devworkspace/solver/che_routing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,12 +302,14 @@ func TestCreateRelocatedObjectsK8S(t *testing.T) {

workspaceMainConfig := gateway.TraefikConfig{}
assert.NoError(t, yaml.Unmarshal([]byte(traefikMainWorkspaceConfig), &workspaceMainConfig))
assert.Len(t, workspaceMainConfig.HTTP.Middlewares, 2)
assert.Len(t, workspaceMainConfig.HTTP.Middlewares, 4)

wsid = "wsid"
mwares = []string{
wsid + gateway.AuthMiddlewareSuffix,
wsid + gateway.StripPrefixMiddlewareSuffix}
wsid + gateway.StripPrefixMiddlewareSuffix,
wsid + gateway.HeadersMiddlewareSuffix,
wsid + gateway.ErrorsMiddlewareSuffix}
for _, mware := range mwares {
assert.Contains(t, workspaceMainConfig.HTTP.Middlewares, mware)

Expand Down Expand Up @@ -392,13 +394,15 @@ func TestCreateRelocatedObjectsOpenshift(t *testing.T) {

workspaceMainConfig := gateway.TraefikConfig{}
assert.NoError(t, yaml.Unmarshal([]byte(traefikMainWorkspaceConfig), &workspaceMainConfig))
assert.Len(t, workspaceMainConfig.HTTP.Middlewares, 3)
assert.Len(t, workspaceMainConfig.HTTP.Middlewares, 5)

wsid = "wsid"
mwares := []string{
wsid + gateway.AuthMiddlewareSuffix,
wsid + gateway.StripPrefixMiddlewareSuffix,
wsid + gateway.HeaderRewriteMiddlewareSuffix}
wsid + gateway.HeaderRewriteMiddlewareSuffix,
wsid + gateway.HeadersMiddlewareSuffix,
wsid + gateway.ErrorsMiddlewareSuffix}
for _, mware := range mwares {
assert.Contains(t, workspaceMainConfig.HTTP.Middlewares, mware)

Expand All @@ -411,6 +415,17 @@ func TestCreateRelocatedObjectsOpenshift(t *testing.T) {
assert.Truef(t, found, "traefik config route doesn't set middleware '%s'", mware)
}

t.Run("testServerTransportInMainWorkspaceRoute", func(t *testing.T) {
serverTransportName := wsid

assert.Len(t, workspaceMainConfig.HTTP.ServersTransports, 1)
assert.Contains(t, workspaceMainConfig.HTTP.ServersTransports, serverTransportName)

assert.Len(t, workspaceMainConfig.HTTP.Services, 1)
assert.Contains(t, workspaceMainConfig.HTTP.Services, wsid)
assert.Equal(t, workspaceMainConfig.HTTP.Services[wsid].LoadBalancer.ServersTransport, serverTransportName)
})

t.Run("testHealthzEndpointInMainWorkspaceRoute", func(t *testing.T) {
healthzName := "wsid-9999-healthz"
assert.Contains(t, workspaceMainConfig.HTTP.Routers, healthzName)
Expand Down
30 changes: 26 additions & 4 deletions pkg/deploy/gateway/traefik_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ type TraefikConfig struct {
}

type TraefikConfigHTTP struct {
Routers map[string]*TraefikConfigRouter `json:"routers"`
Services map[string]*TraefikConfigService `json:"services"`
Middlewares map[string]*TraefikConfigMiddleware `json:"middlewares,omitempty"`
Routers map[string]*TraefikConfigRouter `json:"routers"`
Services map[string]*TraefikConfigService `json:"services"`
Middlewares map[string]*TraefikConfigMiddleware `json:"middlewares,omitempty"`
ServersTransports map[string]*TraefikConfigServersTransport `json:"serversTransports,omitempty"`
}

type TraefikConfigRouter struct {
Expand All @@ -36,11 +37,18 @@ type TraefikConfigService struct {
type TraefikConfigMiddleware struct {
StripPrefix *TraefikConfigStripPrefix `json:"stripPrefix,omitempty"`
ForwardAuth *TraefikConfigForwardAuth `json:"forwardAuth,omitempty"`
Errors *TraefikConfigErrors `json:"errors,omitempty"`
Headers *TraefikConfigHeaders `json:"headers,omitempty"`
Plugin *TraefikPlugin `json:"plugin,omitempty"`
}

type TraefikConfigServersTransport struct {
ForwardingTimeouts *TraefikConfigForwardingTimeouts `json:"forwardingTimeouts"`
}

type TraefikConfigLoadbalancer struct {
Servers []TraefikConfigLoadbalancerServer `json:"servers"`
Servers []TraefikConfigLoadbalancerServer `json:"servers"`
ServersTransport string `json:"serversTransport,omitempty"`
}

type TraefikConfigLoadbalancerServer struct {
Expand All @@ -57,6 +65,16 @@ type TraefikConfigForwardAuth struct {
TLS *TraefikConfigTLS `json:"tls,omitempty"`
}

type TraefikConfigErrors struct {
Status string `json:"status"`
Service string `json:"service"`
Query string `json:"query"`
}

type TraefikConfigHeaders struct {
CustomResponseHeaders map[string]string `json:"customResponseHeaders,omitempty"`
}

type TraefikPlugin struct {
HeaderRewrite *TraefikPluginHeaderRewrite `json:"header-rewrite,omitempty"`
}
Expand All @@ -70,3 +88,7 @@ type TraefikPluginHeaderRewrite struct {
type TraefikConfigTLS struct {
InsecureSkipVerify bool `json:"insecureSkipVerify"`
}

type TraefikConfigForwardingTimeouts struct {
DialTimeout string `json:"dialTimeout"`
}
25 changes: 25 additions & 0 deletions pkg/deploy/gateway/traefik_config_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ const (
StripPrefixMiddlewareSuffix = "-strip-prefix"
HeaderRewriteMiddlewareSuffix = "-header-rewrite"
AuthMiddlewareSuffix = "-auth"
ErrorsMiddlewareSuffix = "-errors"
HeadersMiddlewareSuffix = "-headers"
)

func CreateEmptyTraefikConfig() *TraefikConfig {
Expand Down Expand Up @@ -49,6 +51,7 @@ func (cfg *TraefikConfig) AddComponent(componentName string, rule string, priori
},
},
}

if len(stripPrefixes) > 0 {
cfg.AddStripPrefix(componentName, stripPrefixes)
}
Expand Down Expand Up @@ -101,3 +104,25 @@ func (cfg *TraefikConfig) AddAuth(componentName string, authAddress string) {
},
}
}

func (cfg *TraefikConfig) AddErrors(componentName string, status string, service string, query string) {
middlewareName := componentName + ErrorsMiddlewareSuffix
cfg.HTTP.Routers[componentName].Middlewares = append(cfg.HTTP.Routers[componentName].Middlewares, middlewareName)
cfg.HTTP.Middlewares[middlewareName] = &TraefikConfigMiddleware{
Errors: &TraefikConfigErrors{
Status: status,
Service: service,
Query: query,
},
}
}

func (cfg *TraefikConfig) AddResponseHeaders(componentName string, headers map[string]string) {
middlewareName := componentName + HeadersMiddlewareSuffix
cfg.HTTP.Routers[componentName].Middlewares = append(cfg.HTTP.Routers[componentName].Middlewares, middlewareName)
cfg.HTTP.Middlewares[middlewareName] = &TraefikConfigMiddleware{
Headers: &TraefikConfigHeaders{
CustomResponseHeaders: headers,
},
}
}
32 changes: 32 additions & 0 deletions pkg/deploy/gateway/traefik_config_util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,38 @@ func TestAddOpenShiftTokenCheck(t *testing.T) {
}
}

func TestAddErrors(t *testing.T) {
status := "500-599"
service := "service"
query := "/"

cfg := CreateCommonTraefikConfig(testComponentName, testRule, 1, "http://svc:8080", []string{})
cfg.AddErrors(testComponentName, status, service, query)

assert.Len(t, cfg.HTTP.Routers[testComponentName].Middlewares, 1, *cfg)
assert.Len(t, cfg.HTTP.Middlewares, 1, *cfg)
middlewareName := cfg.HTTP.Routers[testComponentName].Middlewares[0]
if assert.Contains(t, cfg.HTTP.Middlewares, middlewareName, *cfg) && assert.NotNil(t, cfg.HTTP.Middlewares[middlewareName].Errors) {
assert.Equal(t, status+"a", cfg.HTTP.Middlewares[middlewareName].Errors.Status)
assert.Equal(t, service+"a", cfg.HTTP.Middlewares[middlewareName].Errors.Service)
assert.Equal(t, query+"a", cfg.HTTP.Middlewares[middlewareName].Errors.Query)
}
}

func TestAddResponseHeaders(t *testing.T) {
reponseHeaders := map[string]string{"cache-control": "no-store, max-age=0"}

cfg := CreateCommonTraefikConfig(testComponentName, testRule, 1, "http://svc:8080", []string{})
cfg.AddResponseHeaders(testComponentName, reponseHeaders)

assert.Len(t, cfg.HTTP.Routers[testComponentName].Middlewares, 1, *cfg)
assert.Len(t, cfg.HTTP.Middlewares, 1, *cfg)
middlewareName := cfg.HTTP.Routers[testComponentName].Middlewares[0]
if assert.Contains(t, cfg.HTTP.Middlewares, middlewareName, *cfg) && assert.NotNil(t, cfg.HTTP.Middlewares[middlewareName].Headers) {
assert.Equal(t, reponseHeaders, cfg.HTTP.Middlewares[middlewareName].Headers.CustomResponseHeaders)
}
}

func TestMiddlewaresPreserveOrder(t *testing.T) {
t.Run("strip-header", func(t *testing.T) {
cfg := CreateCommonTraefikConfig(testComponentName, testRule, 1, "http://svc:8080", []string{})
Expand Down

0 comments on commit c956fe1

Please sign in to comment.