Add application (#750)

* add schedulercluster to callsystem
* update callsystem rest api
* add cdncluster for callsystem

Signed-off-by: yxxhero <aiopsclub@163.com>
This commit is contained in:
yxxhero 2021-11-19 11:16:45 +08:00 committed by Gaius
parent 206b3244c9
commit 06661c3506
No known key found for this signature in database
GPG Key ID: 8B4E5D1290FA2FFB
12 changed files with 610 additions and 13 deletions

3
go.mod
View File

@ -3,11 +3,9 @@ module d7y.io/dragonfly/v2
go 1.15 go 1.15
require ( require (
github.com/HuKeping/rbtree v0.0.0-20210106022122-8ad34838eb2b
github.com/RichardKnop/machinery v1.10.6 github.com/RichardKnop/machinery v1.10.6
github.com/VividCortex/mysqlerr v1.0.0 github.com/VividCortex/mysqlerr v1.0.0
github.com/agiledragon/gomonkey v2.0.2+incompatible github.com/agiledragon/gomonkey v2.0.2+incompatible
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
github.com/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible github.com/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible
github.com/appleboy/gin-jwt/v2 v2.6.5-0.20210827121450-79689222c755 github.com/appleboy/gin-jwt/v2 v2.6.5-0.20210827121450-79689222c755
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
@ -49,7 +47,6 @@ require (
github.com/onsi/ginkgo v1.16.5 github.com/onsi/ginkgo v1.16.5
github.com/onsi/gomega v1.14.0 github.com/onsi/gomega v1.14.0
github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/go-digest v1.0.0
github.com/pborman/uuid v1.2.1
github.com/pelletier/go-toml v1.8.1 // indirect github.com/pelletier/go-toml v1.8.1 // indirect
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1

6
go.sum
View File

@ -46,8 +46,6 @@ github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/HuKeping/rbtree v0.0.0-20210106022122-8ad34838eb2b h1:zDhQxG7rm8RLgLgi6NpfaVFsop+zxw5hwhbzHr624us=
github.com/HuKeping/rbtree v0.0.0-20210106022122-8ad34838eb2b/go.mod h1:bODsl3NElqKlgf1UkBLj67fYmY5DsqkKrrYm/kMT/6Y=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
@ -70,7 +68,6 @@ github.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3
github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw= github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@ -382,7 +379,6 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
@ -667,8 +663,6 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/getopt v0.0.0-20180729010549-6fdd0a2c7117/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/pborman/getopt v0.0.0-20180729010549-6fdd0a2c7117/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=

View File

@ -117,6 +117,7 @@ func migrate(db *gorm.DB) error {
&model.User{}, &model.User{},
&model.Oauth{}, &model.Oauth{},
&model.Config{}, &model.Config{},
&model.Application{},
) )
} }

View File

@ -0,0 +1,279 @@
/*
* 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 handlers
import (
"net/http"
"github.com/gin-gonic/gin"
// nolint
_ "d7y.io/dragonfly/v2/manager/model"
"d7y.io/dragonfly/v2/manager/types"
)
// @Summary Create Application
// @Description create by json config
// @Tags Application
// @Accept json
// @Produce json
// @Param Application body types.CreateApplicationRequest true "Application"
// @Success 200 {object} model.Application
// @Failure 400
// @Failure 404
// @Failure 500
// @Router /applications [post]
func (h *Handlers) CreateApplication(ctx *gin.Context) {
var json types.CreateApplicationRequest
if err := ctx.ShouldBindJSON(&json); err != nil {
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
return
}
application, err := h.service.CreateApplication(ctx.Request.Context(), json)
if err != nil {
ctx.Error(err) // nolint: errcheck
return
}
ctx.JSON(http.StatusOK, application)
}
// @Summary Destroy Application
// @Description Destroy by id
// @Tags Application
// @Accept json
// @Produce json
// @Param id path string true "id"
// @Success 200
// @Failure 400
// @Failure 404
// @Failure 500
// @Router /applications/{id} [delete]
func (h *Handlers) DestroyApplication(ctx *gin.Context) {
var params types.ApplicationParams
if err := ctx.ShouldBindUri(&params); err != nil {
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
return
}
if err := h.service.DestroyApplication(ctx.Request.Context(), params.ID); err != nil {
ctx.Error(err) // nolint: errcheck
return
}
ctx.Status(http.StatusOK)
}
// @Summary Update Application
// @Description Update by json config
// @Tags Application
// @Accept json
// @Produce json
// @Param id path string true "id"
// @Param Application body types.UpdateApplicationRequest true "Application"
// @Success 200 {object} model.Application
// @Failure 400
// @Failure 404
// @Failure 500
// @Router /applications/{id} [patch]
func (h *Handlers) UpdateApplication(ctx *gin.Context) {
var params types.ApplicationParams
if err := ctx.ShouldBindUri(&params); err != nil {
ctx.Error(err) // nolint: errcheck
return
}
var json types.UpdateApplicationRequest
if err := ctx.ShouldBindJSON(&json); err != nil {
ctx.Error(err) // nolint: errcheck
return
}
application, err := h.service.UpdateApplication(ctx.Request.Context(), params.ID, json)
if err != nil {
ctx.Error(err) // nolint: errcheck
return
}
ctx.JSON(http.StatusOK, application)
}
// @Summary Get Application
// @Description Get Application by id
// @Tags Application
// @Accept json
// @Produce json
// @Param id path string true "id"
// @Success 200 {object} model.Application
// @Failure 400
// @Failure 404
// @Failure 500
// @Router /applications/{id} [get]
func (h *Handlers) GetApplication(ctx *gin.Context) {
var params types.ApplicationParams
if err := ctx.ShouldBindUri(&params); err != nil {
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
return
}
application, err := h.service.GetApplication(ctx.Request.Context(), params.ID)
if err != nil {
ctx.Error(err) // nolint: errcheck
return
}
ctx.JSON(http.StatusOK, application)
}
// @Summary Get Applications
// @Description Get Applications
// @Tags Application
// @Accept json
// @Produce json
// @Param page query int true "current page" default(0)
// @Param per_page query int true "return max item count, default 10, max 50" default(10) minimum(2) maximum(50)
// @Success 200 {object} []model.Application
// @Failure 400
// @Failure 404
// @Failure 500
// @Router /applications [get]
func (h *Handlers) GetApplications(ctx *gin.Context) {
var query types.GetApplicationsQuery
if err := ctx.ShouldBindQuery(&query); err != nil {
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
return
}
h.setPaginationDefault(&query.Page, &query.PerPage)
applications, count, err := h.service.GetApplications(ctx.Request.Context(), query)
if err != nil {
ctx.Error(err) // nolint: errcheck
return
}
h.setPaginationLinkHeader(ctx, query.Page, query.PerPage, int(count))
ctx.JSON(http.StatusOK, applications)
}
// @Summary Add Scheduler to Application
// @Description Add Scheduler to Application
// @Tags Application
// @Accept json
// @Produce json
// @Param id path string true "id"
// @Param scheduler_cluster_id path string true "scheduler cluster id"
// @Success 200
// @Failure 400
// @Failure 404
// @Failure 500
// @Router /applications/{id}/scheduler-clusters/{scheduler_cluster_id} [put]
func (h *Handlers) AddSchedulerClusterToApplication(ctx *gin.Context) {
var params types.AddSchedulerClusterToApplicationParams
if err := ctx.ShouldBindUri(&params); err != nil {
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
return
}
if err := h.service.AddSchedulerClusterToApplication(ctx.Request.Context(), params.ID, params.SchedulerClusterID); err != nil {
ctx.Error(err) // nolint: errcheck
return
}
ctx.Status(http.StatusOK)
}
// @Summary Delete Scheduler to Application
// @Description Delete Scheduler to Application
// @Tags Application
// @Accept json
// @Produce json
// @Param id path string true "id"
// @Param scheduler_cluster_id path string true "scheduler cluster id"
// @Success 200
// @Failure 400
// @Failure 404
// @Failure 500
// @Router /applications/{id}/scheduler-clusters/{scheduler_cluster_id} [delete]
func (h *Handlers) DeleteSchedulerClusterToApplication(ctx *gin.Context) {
var params types.DeleteSchedulerClusterToApplicationParams
if err := ctx.ShouldBindUri(&params); err != nil {
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
return
}
if err := h.service.DeleteSchedulerClusterToApplication(ctx.Request.Context(), params.ID, params.SchedulerClusterID); err != nil {
ctx.Error(err) // nolint: errcheck
return
}
ctx.Status(http.StatusOK)
}
// @Summary Add CDN to Application
// @Description Add CDN to Application
// @Tags Application
// @Accept json
// @Produce json
// @Param id path string true "id"
// @Param cdn_cluster_id path string true "cdn cluster id"
// @Success 200
// @Failure 400
// @Failure 404
// @Failure 500
// @Router /applications/{id}/cdn-clusters/{cdn_cluster_id} [put]
func (h *Handlers) AddCDNClusterToApplication(ctx *gin.Context) {
var params types.AddCDNClusterToApplicationParams
if err := ctx.ShouldBindUri(&params); err != nil {
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
return
}
if err := h.service.AddCDNClusterToApplication(ctx.Request.Context(), params.ID, params.CDNClusterID); err != nil {
ctx.Error(err) // nolint: errcheck
return
}
ctx.Status(http.StatusOK)
}
// @Summary Delete CDN to Application
// @Description Delete CDN to Application
// @Tags Application
// @Accept json
// @Produce json
// @Param id path string true "id"
// @Param cdn_cluster_id path string true "cdn cluster id"
// @Success 200
// @Failure 400
// @Failure 404
// @Failure 500
// @Router /applications/{id}/cdn-clusters/{cdn_cluster_id} [delete]
func (h *Handlers) DeleteCDNClusterToApplication(ctx *gin.Context) {
var params types.DeleteCDNClusterToApplicationParams
if err := ctx.ShouldBindUri(&params); err != nil {
ctx.JSON(http.StatusUnprocessableEntity, gin.H{"errors": err.Error()})
return
}
if err := h.service.DeleteCDNClusterToApplication(ctx.Request.Context(), params.ID, params.CDNClusterID); err != nil {
ctx.Error(err) // nolint: errcheck
return
}
ctx.Status(http.StatusOK)
}

View File

@ -0,0 +1,29 @@
/*
* 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 model
type Application struct {
Model
Name string `gorm:"column:name;type:varchar(256);index:uk_application_name,unique;not null;comment:name" json:"name"`
DownloadRateLimit string `gorm:"column:download_rate_limit;type:varchar(1024);comment:download_rate_limit" json:"download_rate_limit"`
URL string `gorm:"column:url;not null;comment:url" json:"url"`
State string `gorm:"column:state;type:varchar(256);default:'enable';comment:state" json:"state"`
BIO string `gorm:"column:bio;type:varchar(1024);comment:biography" json:"bio"`
UserID uint `gorm:"comment:user id" json:"user_id"`
SchedulerClusters []SchedulerCluster `json:"-"`
CDNClusters []CDNCluster `json:"-"`
}

View File

@ -25,6 +25,7 @@ type CDNCluster struct {
IsDefault bool `gorm:"column:is_default;not null;default:false;comment:default cdn cluster" json:"is_default"` IsDefault bool `gorm:"column:is_default;not null;default:false;comment:default cdn cluster" json:"is_default"`
CDNs []CDN `json:"-"` CDNs []CDN `json:"-"`
SecurityGroupID uint `gorm:"comment:security group id" json:"security_group_id"` SecurityGroupID uint `gorm:"comment:security group id" json:"security_group_id"`
ApplicationID uint `gorm:"comment:application id" json:"application_id"`
SecurityGroup SecurityGroup `json:"-"` SecurityGroup SecurityGroup `json:"-"`
Jobs []Job `gorm:"many2many:job_cdn_cluster;" json:"jobs"` Jobs []Job `gorm:"many2many:job_cdn_cluster;" json:"jobs"`
} }

View File

@ -42,7 +42,10 @@ func Paginate(page, perPage int) func(db *gorm.DB) *gorm.DB {
} }
} }
type JSONMap map[string]interface{} type (
JSONMap map[string]interface{}
Array []string
)
func (m JSONMap) Value() (driver.Value, error) { func (m JSONMap) Value() (driver.Value, error) {
if m == nil { if m == nil {
@ -52,6 +55,14 @@ func (m JSONMap) Value() (driver.Value, error) {
return string(ba), err return string(ba), err
} }
func (a Array) Value() (driver.Value, error) {
if a == nil {
return nil, nil
}
ba, err := a.MarshalJSON()
return string(ba), err
}
func (m *JSONMap) Scan(val interface{}) error { func (m *JSONMap) Scan(val interface{}) error {
var ba []byte var ba []byte
switch v := val.(type) { switch v := val.(type) {
@ -68,6 +79,22 @@ func (m *JSONMap) Scan(val interface{}) error {
return err return err
} }
func (a *Array) Scan(val interface{}) error {
var ba []byte
switch v := val.(type) {
case []byte:
ba = v
case string:
ba = []byte(v)
default:
return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", val))
}
t := []string{}
err := json.Unmarshal(ba, &t)
*a = Array(t)
return err
}
func (m JSONMap) MarshalJSON() ([]byte, error) { func (m JSONMap) MarshalJSON() ([]byte, error) {
if m == nil { if m == nil {
return []byte("null"), nil return []byte("null"), nil
@ -76,6 +103,14 @@ func (m JSONMap) MarshalJSON() ([]byte, error) {
return json.Marshal(t) return json.Marshal(t)
} }
func (a Array) MarshalJSON() ([]byte, error) {
if a == nil {
return []byte("null"), nil
}
t := ([]string)(a)
return json.Marshal(t)
}
func (m *JSONMap) UnmarshalJSON(b []byte) error { func (m *JSONMap) UnmarshalJSON(b []byte) error {
t := map[string]interface{}{} t := map[string]interface{}{}
err := json.Unmarshal(b, &t) err := json.Unmarshal(b, &t)
@ -83,6 +118,13 @@ func (m *JSONMap) UnmarshalJSON(b []byte) error {
return err return err
} }
func (a *Array) UnmarshalJSON(b []byte) error {
t := []string{}
err := json.Unmarshal(b, &t)
*a = Array(t)
return err
}
func (m JSONMap) GormDataType() string { func (m JSONMap) GormDataType() string {
return "jsonmap" return "jsonmap"
} }
@ -90,3 +132,11 @@ func (m JSONMap) GormDataType() string {
func (JSONMap) GormDBDataType(db *gorm.DB, field *schema.Field) string { func (JSONMap) GormDBDataType(db *gorm.DB, field *schema.Field) string {
return "text" return "text"
} }
func (Array) GormDataType() string {
return "array"
}
func (Array) GormDBDataType(db *gorm.DB, field *schema.Field) string {
return "text"
}

View File

@ -29,4 +29,5 @@ type SchedulerCluster struct {
SecurityGroupID uint `gorm:"comment:security group id" json:"security_group_id"` SecurityGroupID uint `gorm:"comment:security group id" json:"security_group_id"`
SecurityGroup SecurityGroup `json:"-"` SecurityGroup SecurityGroup `json:"-"`
Jobs []Job `gorm:"many2many:job_scheduler_cluster;" json:"jobs"` Jobs []Job `gorm:"many2many:job_scheduler_cluster;" json:"jobs"`
ApplicationID uint `gorm:"comment:application id" json:"application_id"`
} }

View File

@ -42,9 +42,7 @@ const (
OtelServiceName = "dragonfly-manager" OtelServiceName = "dragonfly-manager"
) )
var ( var GinLogFileName = "gin.log"
GinLogFileName = "gin.log"
)
func Init(cfg *config.Config, service service.REST, enforcer *casbin.Enforcer) (*gin.Engine, error) { func Init(cfg *config.Config, service service.REST, enforcer *casbin.Enforcer) (*gin.Engine, error) {
// Set mode // Set mode
@ -147,6 +145,18 @@ func Init(cfg *config.Config, service service.REST, enforcer *casbin.Enforcer) (
s.GET(":id", h.GetScheduler) s.GET(":id", h.GetScheduler)
s.GET("", h.GetSchedulers) s.GET("", h.GetSchedulers)
// 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)
cs.PUT(":id/scheduler-clusters/:scheduler_cluster_id", h.AddSchedulerClusterToApplication)
cs.DELETE(":id/scheduler-clusters/:scheduler_cluster_id", h.DeleteSchedulerClusterToApplication)
cs.PUT(":id/cdn-clusters/:cdn_cluster_id", h.AddCDNClusterToApplication)
cs.DELETE(":id/cdn-clusters/:cdn_cluster_id", h.DeleteCDNClusterToApplication)
// CDN Cluster // CDN Cluster
cc := apiv1.Group("/cdn-clusters", jwt.MiddlewareFunc(), rbac) cc := apiv1.Group("/cdn-clusters", jwt.MiddlewareFunc(), rbac)
cc.POST("", h.CreateCDNCluster) cc.POST("", h.CreateCDNCluster)

View File

@ -0,0 +1,161 @@
/*
* 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 service
import (
"context"
"d7y.io/dragonfly/v2/manager/model"
"d7y.io/dragonfly/v2/manager/types"
)
func (s *rest) CreateApplication(ctx context.Context, json types.CreateApplicationRequest) (*model.Application, error) {
application := model.Application{
Name: json.Name,
DownloadRateLimit: json.DownloadRateLimit,
URL: json.URL,
UserID: json.UserID,
BIO: json.BIO,
State: json.State,
}
if err := s.db.WithContext(ctx).Create(&application).Error; err != nil {
return nil, err
}
return &application, nil
}
func (s *rest) DestroyApplication(ctx context.Context, id uint) error {
application := model.Application{}
if err := s.db.WithContext(ctx).First(&application, id).Error; err != nil {
return err
}
if err := s.db.WithContext(ctx).Unscoped().Delete(&model.Application{}, id).Error; err != nil {
return err
}
return nil
}
func (s *rest) UpdateApplication(ctx context.Context, id uint, json types.UpdateApplicationRequest) (*model.Application, error) {
application := model.Application{}
if err := s.db.WithContext(ctx).First(&application, id).Updates(model.Application{
Name: json.Name,
DownloadRateLimit: json.DownloadRateLimit,
URL: json.URL,
State: json.State,
BIO: json.BIO,
UserID: json.UserID,
}).Error; err != nil {
return nil, err
}
return &application, nil
}
func (s *rest) GetApplication(ctx context.Context, id uint) (*model.Application, error) {
application := model.Application{}
if err := s.db.WithContext(ctx).Preload("SchedulerClusters").First(&application, id).Error; err != nil {
return nil, err
}
return &application, nil
}
func (s *rest) GetApplications(ctx context.Context, q types.GetApplicationsQuery) (*[]model.Application, int64, error) {
var count int64
applications := []model.Application{}
if err := s.db.WithContext(ctx).Scopes(model.Paginate(q.Page, q.PerPage)).Preload("SchedulerClusters").Find(&applications).Count(&count).Error; err != nil {
return nil, 0, err
}
return &applications, count, nil
}
func (s *rest) AddSchedulerClusterToApplication(ctx context.Context, id, schedulerClusterID uint) error {
application := model.Application{}
if err := s.db.WithContext(ctx).First(&application, id).Error; err != nil {
return err
}
schedulerCluster := model.SchedulerCluster{}
if err := s.db.WithContext(ctx).First(&schedulerCluster, schedulerClusterID).Error; err != nil {
return err
}
if err := s.db.WithContext(ctx).Model(&application).Association("SchedulerClusters").Append(&schedulerCluster); err != nil {
return err
}
return nil
}
func (s *rest) DeleteSchedulerClusterToApplication(ctx context.Context, id, schedulerClusterID uint) error {
application := model.Application{}
if err := s.db.WithContext(ctx).First(&application, id).Error; err != nil {
return err
}
schedulerCluster := model.SchedulerCluster{}
if err := s.db.WithContext(ctx).First(&schedulerCluster, schedulerClusterID).Error; err != nil {
return err
}
if err := s.db.Model(&application).Association("SchedulerClusters").Delete(&schedulerCluster); err != nil {
return err
}
return nil
}
func (s *rest) AddCDNClusterToApplication(ctx context.Context, id, cdnClusterID uint) error {
application := model.Application{}
if err := s.db.WithContext(ctx).First(&application, id).Error; err != nil {
return err
}
cdnCluster := model.CDNCluster{}
if err := s.db.WithContext(ctx).First(&cdnCluster, cdnClusterID).Error; err != nil {
return err
}
if err := s.db.WithContext(ctx).Model(&application).Association("CDNClusters").Append(&cdnCluster); err != nil {
return err
}
return nil
}
func (s *rest) DeleteCDNClusterToApplication(ctx context.Context, id, cdnClusterID uint) error {
application := model.Application{}
if err := s.db.WithContext(ctx).First(&application, id).Error; err != nil {
return err
}
cdnCluster := model.CDNCluster{}
if err := s.db.WithContext(ctx).First(&cdnCluster, cdnClusterID).Error; err != nil {
return err
}
if err := s.db.Model(&application).Association("CDNClusters").Delete(&cdnCluster); err != nil {
return err
}
return nil
}

View File

@ -116,6 +116,16 @@ type REST interface {
CreateV1Preheat(context.Context, types.CreateV1PreheatRequest) (*types.CreateV1PreheatResponse, error) CreateV1Preheat(context.Context, types.CreateV1PreheatRequest) (*types.CreateV1PreheatResponse, error)
GetV1Preheat(context.Context, string) (*types.GetV1PreheatResponse, error) GetV1Preheat(context.Context, string) (*types.GetV1PreheatResponse, error)
CreateApplication(context.Context, types.CreateApplicationRequest) (*model.Application, error)
DestroyApplication(context.Context, uint) error
UpdateApplication(context.Context, uint, types.UpdateApplicationRequest) (*model.Application, error)
GetApplication(context.Context, uint) (*model.Application, error)
GetApplications(context.Context, types.GetApplicationsQuery) (*[]model.Application, int64, error)
AddSchedulerClusterToApplication(context.Context, uint, uint) error
DeleteSchedulerClusterToApplication(context.Context, uint, uint) error
AddCDNClusterToApplication(context.Context, uint, uint) error
DeleteCDNClusterToApplication(context.Context, uint, uint) error
} }
type rest struct { type rest struct {

View File

@ -0,0 +1,64 @@
/*
* 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 types
type ApplicationParams struct {
ID uint `uri:"id" binding:"required"`
}
type AddSchedulerClusterToApplicationParams struct {
ID uint `uri:"id" binding:"required"`
SchedulerClusterID uint `uri:"scheduler_cluster_id" binding:"required"`
}
type DeleteSchedulerClusterToApplicationParams struct {
ID uint `uri:"id" binding:"required"`
SchedulerClusterID uint `uri:"scheduler_cluster_id" binding:"required"`
}
type AddCDNClusterToApplicationParams struct {
ID uint `uri:"id" binding:"required"`
CDNClusterID uint `uri:"cdn_cluster_id" binding:"required"`
}
type DeleteCDNClusterToApplicationParams struct {
ID uint `uri:"id" binding:"required"`
CDNClusterID uint `uri:"cdn_cluster_id" binding:"required"`
}
type CreateApplicationRequest struct {
Name string `json:"name" binding:"required"`
DownloadRateLimit string `json:"download_rate_limit" binding:"omitempty"`
URL string `json:"url" binding:"omitempty"`
BIO string `json:"bio" binding:"omitempty"`
UserID uint `json:"user_id" binding:"omitempty"`
State string `json:"state" binding:"required,oneof=enable disable"`
}
type UpdateApplicationRequest struct {
Name string `json:"name" binding:"required"`
DownloadRateLimit string `json:"download_rate_limit" binding:"required"`
URL string `json:"url" binding:"required"`
State string `json:"state" binding:"required,oneof=enable disable"`
BIO string `json:"bio" binding:"omitempty"`
UserID uint `json:"user_id" binding:"omitempty"`
}
type GetApplicationsQuery struct {
Page int `form:"page" binding:"omitempty,gte=1"`
PerPage int `form:"per_page" binding:"omitempty,gte=1,lte=50"`
}