Merge pull request #13422 from heww/disable-db-acess-for-ping

feat: skip middlewares require db for ping
This commit is contained in:
He Weiwei 2020-11-05 08:19:49 +08:00 committed by GitHub
commit 955431257c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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

@ -38,6 +38,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.
@ -47,6 +51,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.
@ -63,6 +68,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,
}
)
@ -74,11 +80,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,23 +44,34 @@ 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) {
log := log.G(r.Context())
mode, err := config.AuthMode()
if err == nil {
r = r.WithContext(lib.WithAuthMode(r.Context(), mode))
} else {
log.Warningf("failed to get auth mode: %v", err)
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 {
r = r.WithContext(lib.WithAuthMode(r.Context(), mode))
} else {
log.Warningf("failed to get auth mode: %v", err)
}
for _, generator := range generators {
if ctx := generator.Generate(r); ctx != nil {
r = r.WithContext(security.NewContext(r.Context(), ctx))
break
}
for _, generator := range generators {
if ctx := generator.Generate(r); ctx != nil {
r = r.WithContext(security.NewContext(r.Context(), ctx))
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...)
}