dragonfly/manager/router/router.go

286 lines
8.8 KiB
Go

/*
* Copyright 2020 The Dragonfly Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package router
import (
"net/http"
"time"
casbin "github.com/casbin/casbin/v2"
"github.com/gin-contrib/gzip"
"github.com/gin-contrib/static"
ginzap "github.com/gin-contrib/zap"
"github.com/gin-gonic/gin"
ginprometheus "github.com/mcuadros/go-gin-prometheus"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
logger "d7y.io/dragonfly/v2/internal/dflog"
"d7y.io/dragonfly/v2/internal/ratelimiter"
"d7y.io/dragonfly/v2/manager/config"
"d7y.io/dragonfly/v2/manager/database"
"d7y.io/dragonfly/v2/manager/handlers"
"d7y.io/dragonfly/v2/manager/middlewares"
"d7y.io/dragonfly/v2/manager/service"
"d7y.io/dragonfly/v2/pkg/types"
)
// Init initializes the gin engine with all the routes and middleware.
func Init(cfg *config.Config, logDir string, service service.Service, database *database.Database, enforcer *casbin.Enforcer,
limiter ratelimiter.JobRateLimiter, assets static.ServeFileSystem) (*gin.Engine, error) {
// Set mode.
if !cfg.Verbose {
gin.SetMode(gin.ReleaseMode)
}
r := gin.New()
h := handlers.New(service)
// Prometheus metrics.
p := ginprometheus.NewPrometheus(types.ManagerName)
// URL removes query string.
// Prometheus metrics need to reduce label,
// refer to https://prometheus.io/docs/practices/instrumentation/#do-not-overuse-labels.
p.ReqCntURLLabelMappingFn = func(c *gin.Context) string {
return c.Request.URL.Path
}
p.Use(r)
// Opentelemetry.
if cfg.Tracing.Protocol != "" && cfg.Tracing.Endpoint != "" {
r.Use(otelgin.Middleware(types.ManagerName))
}
// Gin middleware.
r.Use(gin.Recovery())
r.Use(ginzap.Ginzap(logger.GinLogger.Desugar(), time.RFC3339, true))
r.Use(ginzap.RecoveryWithZap(logger.GinLogger.Desugar(), true))
// CORS middleware.
r.Use(middlewares.CORS())
// Server middleware.
r.Use(middlewares.Server())
// gzip middleware.
r.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedExtensions([]string{".js", ".css"})))
// RBAC middleware.
rbac := middlewares.RBAC(enforcer)
jwt, err := middlewares.Jwt(cfg.Auth.JWT, service)
if err != nil {
return nil, err
}
// Personal access token middleware.
personalAccessToken := middlewares.PersonalAccessToken(database.DB)
// Audit middleware.
r.Use(middlewares.Audit(service))
// Error middleware.
r.Use(middlewares.Error())
// Manager view.
r.Use(static.Serve("/", assets))
// API router.
apiv1 := r.Group("/api/v1")
// User.
u := apiv1.Group("/users")
u.PATCH(":id", jwt.MiddlewareFunc(), rbac, h.UpdateUser)
u.GET(":id", jwt.MiddlewareFunc(), rbac, h.GetUser)
u.GET("", jwt.MiddlewareFunc(), rbac, h.GetUsers)
u.POST("signin", jwt.LoginHandler)
u.POST("signout", jwt.LogoutHandler)
u.POST("signup", h.SignUp)
u.GET("signin/:name", h.OauthSignin)
u.GET("signin/:name/callback", h.OauthSigninCallback(jwt))
u.POST("refresh_token", jwt.RefreshHandler)
u.POST(":id/reset_password", h.ResetPassword)
u.GET(":id/roles", jwt.MiddlewareFunc(), rbac, h.GetRolesForUser)
u.PUT(":id/roles/:role", jwt.MiddlewareFunc(), rbac, h.AddRoleToUser)
u.DELETE(":id/roles/:role", jwt.MiddlewareFunc(), rbac, h.DeleteRoleForUser)
// Role.
re := apiv1.Group("/roles", jwt.MiddlewareFunc(), rbac)
re.POST("", h.CreateRole)
re.DELETE(":role", h.DestroyRole)
re.GET(":role", h.GetRole)
re.GET("", h.GetRoles)
re.POST(":role/permissions", h.AddPermissionForRole)
re.DELETE(":role/permissions", h.DeletePermissionForRole)
// Permission.
pm := apiv1.Group("/permissions", jwt.MiddlewareFunc(), rbac)
pm.GET("", h.GetPermissions(r))
// Oauth.
oa := apiv1.Group("/oauth")
oa.POST("", jwt.MiddlewareFunc(), rbac, h.CreateOauth)
oa.DELETE(":id", jwt.MiddlewareFunc(), rbac, h.DestroyOauth)
oa.PATCH(":id", jwt.MiddlewareFunc(), rbac, h.UpdateOauth)
oa.GET(":id", h.GetOauth)
oa.GET("", h.GetOauths)
// Cluster.
c := apiv1.Group("/clusters", jwt.MiddlewareFunc(), rbac)
c.POST("", h.CreateCluster)
c.DELETE(":id", h.DestroyCluster)
c.PATCH(":id", h.UpdateCluster)
c.GET(":id", h.GetCluster)
c.GET("", h.GetClusters)
// Scheduler Cluster.
sc := apiv1.Group("/scheduler-clusters", jwt.MiddlewareFunc(), rbac)
sc.POST("", h.CreateSchedulerCluster)
sc.DELETE(":id", h.DestroySchedulerCluster)
sc.PATCH(":id", h.UpdateSchedulerCluster)
sc.GET(":id", h.GetSchedulerCluster)
sc.GET("", h.GetSchedulerClusters)
sc.PUT(":id/schedulers/:scheduler_id", h.AddSchedulerToSchedulerCluster)
// Scheduler.
s := apiv1.Group("/schedulers", jwt.MiddlewareFunc(), rbac)
s.POST("", h.CreateScheduler)
s.DELETE(":id", h.DestroyScheduler)
s.PATCH(":id", h.UpdateScheduler)
s.GET(":id", h.GetScheduler)
s.GET("", h.GetSchedulers)
// Scheduler Feature.
sf := apiv1.Group("/scheduler-features", jwt.MiddlewareFunc(), rbac)
sf.GET("", h.GetSchedulerFeatures)
// Seed Peer Cluster.
spc := apiv1.Group("/seed-peer-clusters", jwt.MiddlewareFunc(), rbac)
spc.POST("", h.CreateSeedPeerCluster)
spc.DELETE(":id", h.DestroySeedPeerCluster)
spc.PATCH(":id", h.UpdateSeedPeerCluster)
spc.GET(":id", h.GetSeedPeerCluster)
spc.GET("", h.GetSeedPeerClusters)
spc.PUT(":id/seed-peers/:seed_peer_id", h.AddSeedPeerToSeedPeerCluster)
spc.PUT(":id/scheduler-clusters/:scheduler_cluster_id", h.AddSchedulerClusterToSeedPeerCluster)
// Seed Peer.
sp := apiv1.Group("/seed-peers", jwt.MiddlewareFunc(), rbac)
sp.POST("", h.CreateSeedPeer)
sp.DELETE(":id", h.DestroySeedPeer)
sp.PATCH(":id", h.UpdateSeedPeer)
sp.GET(":id", h.GetSeedPeer)
sp.GET("", h.GetSeedPeers)
// Peer.
peer := apiv1.Group("/peers", jwt.MiddlewareFunc(), rbac)
peer.POST("", h.CreatePeer)
peer.DELETE(":id", h.DestroyPeer)
peer.GET(":id", h.GetPeer)
peer.GET("", h.GetPeers)
// Bucket.
bucket := apiv1.Group("/buckets", jwt.MiddlewareFunc(), rbac)
bucket.POST("", h.CreateBucket)
bucket.DELETE(":id", h.DestroyBucket)
bucket.GET(":id", h.GetBucket)
bucket.GET("", h.GetBuckets)
// Config.
config := apiv1.Group("/configs", jwt.MiddlewareFunc(), rbac)
config.POST("", h.CreateConfig)
config.DELETE(":id", h.DestroyConfig)
config.PATCH(":id", h.UpdateConfig)
config.GET(":id", h.GetConfig)
config.GET("", h.GetConfigs)
// TODO Add auth to the following routes and fix the tests.
// Job.
job := apiv1.Group("/jobs")
job.POST("", middlewares.CreateJobRateLimiter(limiter), h.CreateJob)
job.DELETE(":id", h.DestroyJob)
job.PATCH(":id", h.UpdateJob)
job.GET(":id", h.GetJob)
job.GET("", h.GetJobs)
// Application.
cs := apiv1.Group("/applications", jwt.MiddlewareFunc(), rbac)
cs.POST("", h.CreateApplication)
cs.DELETE(":id", h.DestroyApplication)
cs.PATCH(":id", h.UpdateApplication)
cs.GET(":id", h.GetApplication)
cs.GET("", h.GetApplications)
// Personal Access Token.
pat := apiv1.Group("/personal-access-tokens", jwt.MiddlewareFunc(), rbac)
pat.POST("", h.CreatePersonalAccessToken)
pat.DELETE(":id", h.DestroyPersonalAccessToken)
pat.PATCH(":id", h.UpdatePersonalAccessToken)
pat.GET(":id", h.GetPersonalAccessToken)
pat.GET("", h.GetPersonalAccessTokens)
// Persistent Cache Task.
pc := apiv1.Group("/persistent-cache-tasks", jwt.MiddlewareFunc(), rbac)
pc.DELETE(":id", h.DestroyPersistentCacheTask)
pc.GET(":id", h.GetPersistentCacheTask)
pc.GET("", h.GetPersistentCacheTasks)
// Audit.
at := apiv1.Group("/audits", jwt.MiddlewareFunc(), rbac)
at.GET("", h.GetAudits)
// Open API router.
oapiv1 := r.Group("/oapi/v1")
// Job.
ojob := oapiv1.Group("/jobs", personalAccessToken)
ojob.POST("", middlewares.CreateJobRateLimiter(limiter), h.CreateJob)
ojob.DELETE(":id", h.DestroyJob)
ojob.PATCH(":id", h.UpdateJob)
ojob.GET(":id", h.GetJob)
ojob.GET("", h.GetJobs)
// Cluster.
oc := oapiv1.Group("/clusters", personalAccessToken)
oc.POST("", h.CreateCluster)
oc.DELETE(":id", h.DestroyCluster)
oc.PATCH(":id", h.UpdateCluster)
oc.GET(":id", h.GetCluster)
oc.GET("", h.GetClusters)
// TODO Remove this api.
// Compatible with the V1 preheat.
pv1 := r.Group("/preheats")
r.GET("_ping", h.GetHealth)
pv1.POST("", h.CreateV1Preheat)
pv1.GET(":id", h.GetV1Preheat)
// Health Check.
r.GET("/healthy", h.GetHealth)
// Swagger.
apiSeagger := ginSwagger.URL("/swagger/doc.json")
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, apiSeagger))
// Fallback to manager view.
r.NoRoute(func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "/")
})
return r, nil
}