Answer image requests with 503 in maintenance mode (closes #71)
check / check (push) Successful in 3m52s
check / check (push) Successful in 3m52s
maintenance_mode was only reported by the health check; every request was still served. One middleware in routes.go, applied to /v1/image/ and /v1/e/ only, now answers them with 503, a Retry-After of MaintenanceRetryAfterSeconds and the JSON error body while it is on. It calls Server.MaintenanceMode(), which had no caller. The health check stays 200 and reports maintenance_mode: the image's Docker HEALTHCHECK requests it, a 503 there would make the container unhealthy, and upaas marks a deploy failed when its container is unhealthy. The login and URL generator pages and /metrics keep working. Documented in README.md and config.example.yml. Model: opus-5-5
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
sentryhttp "github.com/getsentry/sentry-go/http"
|
||||
@@ -17,6 +19,10 @@ import (
|
||||
// make per minute; the next is refused with 429 Too Many Requests.
|
||||
const LoginAttemptsPerMinute = 5
|
||||
|
||||
// MaintenanceRetryAfterSeconds is the Retry-After, in seconds, sent with
|
||||
// the 503 that the image routes answer while maintenance mode is on.
|
||||
const MaintenanceRetryAfterSeconds = 300
|
||||
|
||||
// SetupRoutes configures all HTTP routes.
|
||||
func (s *Server) SetupRoutes() {
|
||||
s.router = chi.NewRouter()
|
||||
@@ -68,15 +74,23 @@ func (s *Server) SetupRoutes() {
|
||||
|
||||
s.router.Get("/logout", s.h.HandleLogout())
|
||||
|
||||
// Main image proxy route
|
||||
// /v1/image/<host>/<path>/<width>x<height>.<format>
|
||||
s.router.Get("/v1/image/*", s.h.HandleImage())
|
||||
s.router.Head("/v1/image/*", s.h.HandleImage())
|
||||
// Image routes, refused while maintenance mode is on. Only these: the
|
||||
// image's Docker HEALTHCHECK requests the health check, a 503 there
|
||||
// would make the container unhealthy, and upaas marks a deploy failed
|
||||
// when its container is unhealthy.
|
||||
s.router.Group(func(r chi.Router) {
|
||||
r.Use(s.refuseDuringMaintenance)
|
||||
|
||||
// Encrypted image URL route
|
||||
// The trailing filename (e.g., /img.jpg) is ignored but helps
|
||||
// browsers with content type
|
||||
s.router.Get("/v1/e/{token}/*", s.h.HandleImageEnc())
|
||||
// Main image proxy route
|
||||
// /v1/image/<host>/<path>/<width>x<height>.<format>
|
||||
r.Get("/v1/image/*", s.h.HandleImage())
|
||||
r.Head("/v1/image/*", s.h.HandleImage())
|
||||
|
||||
// Encrypted image URL route
|
||||
// The trailing filename (e.g., /img.jpg) is ignored but helps
|
||||
// browsers with content type
|
||||
r.Get("/v1/e/{token}/*", s.h.HandleImageEnc())
|
||||
})
|
||||
|
||||
// Metrics endpoint with auth
|
||||
if s.config.MetricsUsername != "" {
|
||||
@@ -86,3 +100,30 @@ func (s *Server) SetupRoutes() {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// refuseDuringMaintenance answers a request with 503 Service Unavailable,
|
||||
// a Retry-After header and a JSON error body while maintenance mode is on,
|
||||
// and passes it on otherwise. The body has the fields of the JSON errors
|
||||
// the image handlers send.
|
||||
func (s *Server) refuseDuringMaintenance(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if !s.MaintenanceMode() {
|
||||
next.ServeHTTP(w, r)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Retry-After", strconv.Itoa(MaintenanceRetryAfterSeconds))
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
|
||||
err := json.NewEncoder(w).Encode(map[string]any{
|
||||
"error": "down for maintenance, try again later",
|
||||
"status": http.StatusServiceUnavailable,
|
||||
"timestamp": time.Now().UTC().Format(time.RFC3339),
|
||||
})
|
||||
if err != nil {
|
||||
s.log.Error("json encode error", "error", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user