feat: skip middlewares require db for ping

The ping endpoint will be blocked when DB conns reach the max open conns
of the sql.DB which will make ping request timeout,
so skip the middlewares which will require DB conn.

Signed-off-by: He Weiwei <hweiwei@vmware.com>
This commit is contained in:
He Weiwei 2020-11-03 01:58:59 +00:00
parent 9a38dca1a6
commit 0c5aedb3d9
3 changed files with 41 additions and 23 deletions

View File

@ -214,7 +214,7 @@ func init() {
mockServer := test.NewJobServiceServer()
defer mockServer.Close()
chain := middleware.Chain(orm.Middleware(), security.Middleware())
chain := middleware.Chain(orm.Middleware(), security.Middleware(), security.UnauthorizedMiddleware())
handler = chain(beego.BeeApp.Handlers)
}

View File

@ -37,6 +37,10 @@ var (
match = regexp.MustCompile
numericRegexp = match(`[0-9]+`)
// The ping endpoint will be blocked when DB conns reach the max open conns of the sql.DB
// which will make ping request timeout, so skip the middlewares which will require DB conn.
pingSkipper = middleware.MethodAndPathSkipper(http.MethodGet, match("^/api/v2.0/ping"))
// dbTxSkippers skip the transaction middleware for GET Blob, PATCH Blob Upload and PUT Blob Upload APIs
// because the APIs may take a long time to run, enable the transaction middleware in them will hold the database connections
// until the API finished, this behavior may eat all the database connections.
@ -46,6 +50,7 @@ var (
middleware.MethodAndPathSkipper(http.MethodGet, distribution.BlobURLRegexp),
middleware.MethodAndPathSkipper(http.MethodPatch, distribution.BlobUploadURLRegexp),
middleware.MethodAndPathSkipper(http.MethodPut, distribution.BlobUploadURLRegexp),
pingSkipper,
}
// readonlySkippers skip the post request when harbor sets to readonly.
@ -62,6 +67,7 @@ var (
middleware.MethodAndPathSkipper(http.MethodPost, match("^/service/notifications/jobs/retention/task/"+numericRegexp.String())),
middleware.MethodAndPathSkipper(http.MethodPost, match("^/service/notifications/jobs/schedules/"+numericRegexp.String())),
middleware.MethodAndPathSkipper(http.MethodPost, match("^/service/notifications/jobs/webhook/"+numericRegexp.String())),
pingSkipper,
}
)
@ -72,11 +78,12 @@ func MiddleWares() []beego.MiddleWare {
log.Middleware(),
session.Middleware(),
csrf.Middleware(),
orm.Middleware(),
notification.Middleware(), // notification must ahead of transaction ensure the DB transaction execution complete
orm.Middleware(pingSkipper),
notification.Middleware(pingSkipper), // notification must ahead of transaction ensure the DB transaction execution complete
transaction.Middleware(dbTxSkippers...),
artifactinfo.Middleware(),
security.Middleware(),
security.Middleware(pingSkipper),
security.UnauthorizedMiddleware(),
readonly.Middleware(readonlySkippers...),
}
}

View File

@ -21,6 +21,7 @@ import (
"github.com/goharbor/harbor/src/core/config"
"github.com/goharbor/harbor/src/lib"
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/server/middleware"
)
var (
@ -34,7 +35,6 @@ var (
&basicAuth{},
&session{},
&proxyCacheSecret{},
&unauthorized{},
}
)
@ -44,9 +44,8 @@ type generator interface {
}
// Middleware returns a security context middleware that populates the security context into the request context
func Middleware() func(http.Handler) http.Handler {
return func(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
func Middleware(skippers ...middleware.Skipper) func(http.Handler) http.Handler {
return middleware.New(func(w http.ResponseWriter, r *http.Request, next http.Handler) {
log := log.G(r.Context())
mode, err := config.AuthMode()
if err == nil {
@ -60,7 +59,19 @@ func Middleware() func(http.Handler) http.Handler {
break
}
}
handler.ServeHTTP(w, r)
})
next.ServeHTTP(w, r)
}, skippers...)
}
// UnauthorizedMiddleware returns a security context middleware
// that populates the unauthorized security context when not security context found in the request context
func UnauthorizedMiddleware(skippers ...middleware.Skipper) func(http.Handler) http.Handler {
return middleware.New(func(w http.ResponseWriter, r *http.Request, next http.Handler) {
if _, ok := security.FromContext(r.Context()); !ok {
u := &unauthorized{}
r = r.WithContext(security.NewContext(r.Context(), u.Generate(r)))
}
next.ServeHTTP(w, r)
}, skippers...)
}