This is an automated email from the ASF dual-hosted git repository.
robocanic pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/dubbo-admin.git
The following commit(s) were added to refs/heads/develop by this push:
new 4aae2061 feat: Defined a unified error (#1353)
4aae2061 is described below
commit 4aae2061efc9cd7a4ca406c729c481e18c3d6fe0
Author: robb <[email protected]>
AuthorDate: Sun Nov 9 20:08:49 2025 +0800
feat: Defined a unified error (#1353)
* feat: unified error code; separate application handler and service into
parts
* fix: license header lack
* fix: copilot review err fix
* fix: simplify the if-else condition
* chore: rename Error() to String()
* chore: add extra String() in Error
* feat: 新增listMeshes接口
* fix: copilot review
---
app/dubbo-admin/dubbo-admin.yaml | 24 +-
pkg/common/bizerror/common.go | 35 +-
pkg/common/bizerror/{common.go => error.go} | 55 ++-
pkg/config/discovery/config.go | 23 +-
pkg/console/component.go | 4 +-
pkg/console/handler/application.go | 458 ++++-----------------
pkg/console/handler/auth.go | 38 +-
.../bizerror/common.go => console/handler/mesh.go} | 52 +--
pkg/console/model/application.go | 12 +
pkg/console/model/common.go | 28 +-
pkg/console/model/condition_rule.go | 13 +-
pkg/console/model/configurator_rule.go | 9 +-
.../bizerror/common.go => console/model/mesh.go} | 40 +-
pkg/console/model/tag_rule.go | 8 +-
pkg/console/router/router.go | 5 +-
pkg/console/service/application.go | 408 ++++++++++++++++++
.../bizerror/common.go => console/util/error.go} | 44 +-
17 files changed, 650 insertions(+), 606 deletions(-)
diff --git a/app/dubbo-admin/dubbo-admin.yaml b/app/dubbo-admin/dubbo-admin.yaml
index 932304ea..17895772 100644
--- a/app/dubbo-admin/dubbo-admin.yaml
+++ b/app/dubbo-admin/dubbo-admin.yaml
@@ -39,19 +39,23 @@ console:
store:
type: memory
discovery:
- - type: nacos
- id: nacos-44.33
- address:
- registry: nacos://47.76.94.134:8848?username=nacos&password=nacos
- configCenter: nacos://47.76.94.134:8848?username=nacos&password=nacos
- metadataReport: nacos://47.76.94.134:8848?username=nacos&password=nacos
-
- - type: etcd
- id: etcd-44.33
- address: http://127.0.0.1:2379
+# - type: nacos
+# name: nacos-44.33
+# address:
+# registry: nacos://47.76.94.134:8848?username=nacos&password=nacos
+# configCenter: nacos://47.76.94.134:8848?username=nacos&password=nacos
+# metadataReport: nacos://47.76.94.134:8848?username=nacos&password=nacos
+#
+# - type: etcd
+# name: etcd-44.33
+# address:
+# registry: http://127.0.0.1:2379
+# configCenter: http://127.0.0.1:2379
+# metadataReport: http://127.0.0.1:2379
# mock discovery is only for development
- type: mock
+ name: mockRegistry
engine:
name: k8s1.28.6
type: kubernetes
diff --git a/pkg/common/bizerror/common.go b/pkg/common/bizerror/common.go
index 1f993c4b..b8201e95 100644
--- a/pkg/common/bizerror/common.go
+++ b/pkg/common/bizerror/common.go
@@ -18,38 +18,15 @@
package bizerror
import (
- "errors"
"fmt"
)
-type AssertionError struct {
- msg string
+func NewAssertionError(expected, actual interface{}) Error {
+ return NewBizError(UnknownError, fmt.Sprintf("type assertion error,
expected:%v, actual:%v", expected, actual))
}
-
-func NewAssertionError(expected, actual interface{}) error {
- return &AssertionError{
- msg: fmt.Sprintf("type assertion error, expected:%v,
actual:%v", expected, actual),
- }
-}
-
-func (e *AssertionError) Error() string {
- return e.msg
-}
-
-type MeshNotFoundError struct {
- Mesh string
+func NewUnauthorizedError() Error {
+ return NewBizError(Unauthorized, "no access, please login")
}
-
-func (m *MeshNotFoundError) Error() string {
- return fmt.Sprintf("mesh of name %s is not found", m.Mesh)
-}
-
-func MeshNotFound(meshName string) error {
- return &MeshNotFoundError{meshName}
-}
-
-func IsMeshNotFound(err error) bool {
- var meshNotFoundError *MeshNotFoundError
- ok := errors.As(err, &meshNotFoundError)
- return ok
+func MeshNotFoundError(mesh string) Error {
+ return NewBizError(UnknownError, fmt.Sprintf("mesh of name %s is not
found", mesh))
}
diff --git a/pkg/common/bizerror/common.go b/pkg/common/bizerror/error.go
similarity index 50%
copy from pkg/common/bizerror/common.go
copy to pkg/common/bizerror/error.go
index 1f993c4b..b65b023d 100644
--- a/pkg/common/bizerror/common.go
+++ b/pkg/common/bizerror/error.go
@@ -17,39 +17,50 @@
package bizerror
-import (
- "errors"
- "fmt"
+type Error interface {
+ Code() ErrorCode
+ Message() string
+ Error() string
+ String() string
+}
+
+type ErrorCode string
+
+const (
+ UnknownError ErrorCode = "UnknownError"
+ InvalidArgument ErrorCode = "InvalidArgument"
+ StoreError ErrorCode = "StoreError"
+ AppNotFound ErrorCode = "AppNotFound"
+ Unauthorized ErrorCode = "Unauthorized"
+ SessionError ErrorCode = "SessionError"
)
-type AssertionError struct {
- msg string
+type bizError struct {
+ code ErrorCode
+ message string
}
-func NewAssertionError(expected, actual interface{}) error {
- return &AssertionError{
- msg: fmt.Sprintf("type assertion error, expected:%v,
actual:%v", expected, actual),
- }
-}
+var _ Error = &bizError{}
-func (e *AssertionError) Error() string {
- return e.msg
+func NewBizError(code ErrorCode, message string) Error {
+ return &bizError{
+ code: code,
+ message: message,
+ }
}
-type MeshNotFoundError struct {
- Mesh string
+func (b *bizError) Code() ErrorCode {
+ return b.code
}
-func (m *MeshNotFoundError) Error() string {
- return fmt.Sprintf("mesh of name %s is not found", m.Mesh)
+func (b *bizError) Message() string {
+ return b.message
}
-func MeshNotFound(meshName string) error {
- return &MeshNotFoundError{meshName}
+func (b *bizError) Error() string {
+ return b.String()
}
-func IsMeshNotFound(err error) bool {
- var meshNotFoundError *MeshNotFoundError
- ok := errors.As(err, &meshNotFoundError)
- return ok
+func (b *bizError) String() string {
+ return string(b.code) + ": " + b.message
}
diff --git a/pkg/config/discovery/config.go b/pkg/config/discovery/config.go
index fe4dfd00..ee63eed4 100644
--- a/pkg/config/discovery/config.go
+++ b/pkg/config/discovery/config.go
@@ -17,7 +17,12 @@
package discovery
-import "github.com/apache/dubbo-admin/pkg/config"
+import (
+ "github.com/duke-git/lancet/v2/strutil"
+
+ "github.com/apache/dubbo-admin/pkg/common/bizerror"
+ "github.com/apache/dubbo-admin/pkg/config"
+)
type Type string
@@ -31,9 +36,9 @@ const (
// Config defines Discovery configuration
type Config struct {
config.BaseConfig
- Name string `json:"name"`
- Type Type `json:"type"`
- Address AddressConfig
+ Name string `json:"name"`
+ Type Type `json:"type"`
+ Address AddressConfig `json:"address"`
}
// AddressConfig defines Discovery Engine address
@@ -54,3 +59,13 @@ func DefaultDiscoveryEnginConfig() *Config {
},
}
}
+
+func (c *Config) Validate() error {
+ if strutil.IsBlank(c.Name) {
+ return bizerror.NewBizError(bizerror.InvalidArgument,
"discovery name is needed")
+ }
+ if strutil.IsBlank(string(c.Type)) {
+ return bizerror.NewBizError(bizerror.InvalidArgument,
"discovery type is needed")
+ }
+ return nil
+}
diff --git a/pkg/console/component.go b/pkg/console/component.go
index 1672b95f..985b78f6 100644
--- a/pkg/console/component.go
+++ b/pkg/console/component.go
@@ -30,6 +30,7 @@ import (
"github.com/gin-gonic/gin"
ui "github.com/apache/dubbo-admin/app/dubbo-ui"
+ "github.com/apache/dubbo-admin/pkg/common/bizerror"
"github.com/apache/dubbo-admin/pkg/config/console"
consolectx "github.com/apache/dubbo-admin/pkg/console/context"
"github.com/apache/dubbo-admin/pkg/console/model"
@@ -130,7 +131,8 @@ func (c *consoleWebServer) authMiddleware() gin.HandlerFunc
{
session := sessions.Default(c)
user := session.Get("user")
if user == nil {
- c.JSON(http.StatusUnauthorized,
model.NewUnauthorizedResp())
+ authErr := bizerror.NewBizError(bizerror.Unauthorized,
"no access, please login")
+ c.JSON(http.StatusUnauthorized,
model.NewBizErrorResp(authErr))
c.Abort()
return
}
diff --git a/pkg/console/handler/application.go
b/pkg/console/handler/application.go
index 1eedc906..4ad2985a 100644
--- a/pkg/console/handler/application.go
+++ b/pkg/console/handler/application.go
@@ -18,32 +18,29 @@
package handler
import (
+ "errors"
"net/http"
"strconv"
"github.com/duke-git/lancet/v2/strutil"
"github.com/gin-gonic/gin"
- meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1"
consolectx "github.com/apache/dubbo-admin/pkg/console/context"
"github.com/apache/dubbo-admin/pkg/console/model"
"github.com/apache/dubbo-admin/pkg/console/service"
- "github.com/apache/dubbo-admin/pkg/core/consts"
- "github.com/apache/dubbo-admin/pkg/core/logger"
- meshresource
"github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1"
- corestore "github.com/apache/dubbo-admin/pkg/core/store"
+ "github.com/apache/dubbo-admin/pkg/console/util"
)
func GetApplicationDetail(ctx consolectx.Context) gin.HandlerFunc {
return func(c *gin.Context) {
req := &model.ApplicationDetailReq{}
if err := c.ShouldBindQuery(req); err != nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
+ util.HandleArgumentError(c, err)
return
}
resp, err := service.GetApplicationDetail(ctx, req)
if err != nil {
- c.JSON(http.StatusInternalServerError,
model.NewErrorResp(err.Error()))
+ util.HandleServiceError(c, err)
return
}
c.JSON(http.StatusOK, model.NewSuccessResp(resp))
@@ -54,13 +51,13 @@ func GetApplicationTabInstanceInfo(ctx consolectx.Context)
gin.HandlerFunc {
return func(c *gin.Context) {
req := model.NewApplicationTabInstanceInfoReq()
if err := c.ShouldBindQuery(req); err != nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
+ util.HandleArgumentError(c, err)
return
}
resp, err := service.GetAppInstanceInfo(ctx, req)
if err != nil {
- c.JSON(http.StatusInternalServerError,
model.NewErrorResp(err.Error()))
+ util.HandleServiceError(c, err)
return
}
@@ -72,12 +69,12 @@ func GetApplicationServiceForm(ctx consolectx.Context)
gin.HandlerFunc {
return func(c *gin.Context) {
req := model.NewApplicationServiceFormReq()
if err := c.ShouldBindQuery(req); err != nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
+ util.HandleArgumentError(c, err)
return
}
resp, err := service.GetAppServiceInfo(ctx, req)
if err != nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
+ util.HandleServiceError(c, err)
return
}
c.JSON(http.StatusOK, model.NewSuccessResp(resp))
@@ -88,468 +85,159 @@ func ApplicationSearch(ctx consolectx.Context)
gin.HandlerFunc {
return func(c *gin.Context) {
req := model.NewApplicationSearchReq()
if err := c.ShouldBindQuery(req); err != nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
+ util.HandleArgumentError(c, err)
return
}
resp, err := service.SearchApplications(ctx, req)
if err != nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
+ util.HandleServiceError(c, err)
return
}
c.JSON(http.StatusOK, model.NewSuccessResp(resp))
}
}
-func isAppOperatorLogOpened(conf *meshproto.OverrideConfig, appName string)
bool {
- if conf.Side != consts.SideProvider ||
- conf.Parameters == nil ||
- conf.Match == nil ||
- conf.Match.Application == nil ||
- conf.Match.Application.Oneof == nil ||
- len(conf.Match.Application.Oneof) != 1 ||
- conf.Match.Application.Oneof[0].Exact != appName {
- return false
- } else if val, ok := conf.Parameters[`accesslog`]; !ok || val != `true`
{
- return false
- }
- return true
-}
-
-func ApplicationConfigOperatorLogPut(ctx consolectx.Context) gin.HandlerFunc {
+func ApplicationConfigAccessLogPut(ctx consolectx.Context) gin.HandlerFunc {
return func(c *gin.Context) {
- var (
- appName string
- operatorLogOpen bool
- isNotExist = false
- mesh string
- )
- appName = c.Query("appName")
- mesh = c.Query("mesh")
- operatorLogOpen, err :=
strconv.ParseBool(c.Query("operatorLog"))
- if err != nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
+ appName := c.Query("appName")
+ if strutil.IsBlank(appName) {
+ util.HandleArgumentError(c, errors.New("appName is
required"))
return
}
- appConfiguratorName := appName + consts.ConfiguratorRuleSuffix
- res, err := service.GetConfigurator(ctx, appConfiguratorName,
mesh)
- if err != nil {
- if corestore.IsResourceNotFound(err) {
- // check app exists
- data, err := service.GetApplicationDetail(ctx,
&model.ApplicationDetailReq{AppName: appName})
- if err != nil || data == nil {
- c.JSON(http.StatusNotFound,
model.NewErrorResp(err.Error()))
- return
- }
- res =
meshresource.NewDynamicConfigResourceWithAttributes(appConfiguratorName, mesh)
- res.Spec = &meshproto.DynamicConfig{
- Key: appName,
- Scope: consts.ScopeApplication,
- ConfigVersion:
consts.ConfiguratorVersionV3,
- Enabled: true,
- Configs:
make([]*meshproto.OverrideConfig, 0),
- }
- isNotExist = true
- } else {
- c.JSON(http.StatusInternalServerError,
model.NewErrorResp(err.Error()))
- return
- }
+ mesh := c.Query("mesh")
+ if strutil.IsBlank(mesh) {
+ util.HandleArgumentError(c, errors.New("mesh is
required"))
+ return
}
- // append or remove
- if operatorLogOpen {
- // check is already exist
- alreadyExist := false
- res.Spec.RangeConfig(func(conf
*meshproto.OverrideConfig) (isStop bool) {
- alreadyExist = isAppOperatorLogOpened(conf,
appName)
- return alreadyExist
- })
- if alreadyExist {
- c.JSON(http.StatusOK, model.NewSuccessResp(nil))
- return
- }
- if res.Spec.Configs == nil {
- res.Spec.Configs =
make([]*meshproto.OverrideConfig, 0)
- }
- res.Spec.Configs = append(res.Spec.Configs,
&meshproto.OverrideConfig{
- Side: consts.SideProvider,
- Parameters: map[string]string{`accesslog`:
`true`},
- Enabled: true,
- Match: &meshproto.ConditionMatch{
- Application: &meshproto.ListStringMatch{
- Oneof: []*meshproto.StringMatch{
- {
- Exact: appName,
- },
- }}},
- XGenerateByCp: true,
- })
- } else {
- res.Spec.RangeConfigsToRemove(func(conf
*meshproto.OverrideConfig) (IsRemove bool) {
- if conf == nil {
- return true
- }
- return isAppOperatorLogOpened(conf, appName)
- })
+ operatorLogOpen, err :=
strconv.ParseBool(c.Query("operatorLog"))
+ if err != nil {
+ util.HandleArgumentError(c, err)
+ return
}
- // restore
- if isNotExist {
- err = service.CreateConfigurator(ctx, appName, res)
- if err != nil {
- c.JSON(http.StatusInternalServerError,
model.NewErrorResp(err.Error()))
- return
- }
- } else {
- err = service.UpdateConfigurator(ctx, appName, res)
- if err != nil {
- c.JSON(http.StatusInternalServerError,
model.NewErrorResp(err.Error()))
- return
- }
+ if err := service.UpInsertAppAccessLog(ctx, appName,
operatorLogOpen, mesh); err != nil {
+ util.HandleServiceError(c, err)
+ return
}
- c.JSON(http.StatusOK, model.NewSuccessResp(nil))
+ c.JSON(http.StatusOK, model.NewSuccessResp(true))
}
}
-func ApplicationConfigOperatorLogGet(ctx consolectx.Context) gin.HandlerFunc {
+func ApplicationConfigAccessLogGet(ctx consolectx.Context) gin.HandlerFunc {
return func(c *gin.Context) {
appName := c.Query("appName")
- if appName == "" {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp("appName is required"))
+ if strutil.IsBlank(appName) {
+ util.HandleArgumentError(c, errors.New("appName is
required"))
return
}
mesh := c.Query("mesh")
if strutil.IsBlank(mesh) {
- c.JSON(http.StatusBadRequest, model.NewErrorResp("mesh
is required"))
+ util.HandleArgumentError(c, errors.New("mesh is
required"))
return
}
- appConfiguratorName := appName + consts.ConfiguratorRuleSuffix
- res, err := service.GetConfigurator(ctx, appConfiguratorName,
mesh)
+ resp, err := service.GetAppAccessLog(ctx, appName, mesh)
if err != nil {
- if corestore.IsResourceNotFound(err) {
- c.JSON(http.StatusOK,
model.NewSuccessResp(map[string]interface{}{"operatorLog": false}))
- return
- }
- c.JSON(http.StatusNotFound,
model.NewErrorResp(err.Error()))
+ util.HandleServiceError(c, err)
return
}
- isExist := false
- res.Spec.RangeConfig(func(conf *meshproto.OverrideConfig)
(isStop bool) {
- if isExist = isAppOperatorLogOpened(conf, appName);
isExist {
- return true
- }
- return false
- })
- c.JSON(http.StatusOK,
model.NewSuccessResp(map[string]interface{}{"operatorLog": isExist}))
+ c.JSON(http.StatusOK, model.NewSuccessResp(resp))
}
}
func ApplicationConfigFlowWeightGET(ctx consolectx.Context) gin.HandlerFunc {
return func(c *gin.Context) {
- var (
- appName string
- mesh string
- resp = struct {
- FlowWeightSets []model.FlowWeightSet
`json:"flowWeightSets"`
- }{}
- )
- appName = c.Query("appName")
- mesh = c.Query("mesh")
- if appName == "" {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp("appName is required"))
+ appName := c.Query("appName")
+ mesh := c.Query("mesh")
+ if strutil.IsBlank(appName) {
+ util.HandleArgumentError(c, errors.New("appName is
required"))
return
}
-
- resp.FlowWeightSets = make([]model.FlowWeightSet, 0)
- appConfiguratorName := appName + consts.ConfiguratorRuleSuffix
- res, err := service.GetConfigurator(ctx, appConfiguratorName,
mesh)
- if err != nil {
- if corestore.IsResourceNotFound(err) {
- c.JSON(http.StatusOK,
model.NewSuccessResp(resp))
- return
- }
- c.JSON(http.StatusNotFound,
model.NewErrorResp(err.Error()))
+ if strutil.IsBlank(mesh) {
+ util.HandleArgumentError(c, errors.New("mesh is
required"))
return
}
-
- weight := 0
- res.Spec.RangeConfig(func(conf *meshproto.OverrideConfig)
(isStop bool) {
- if isFlowWeight(conf) {
- weight, err =
strconv.Atoi(conf.Parameters[`weight`])
- if err != nil {
- logger.Error("parse weight failed", err)
- return true
- }
- scope := make([]model.ParamMatch, 0,
len(conf.Match.Param))
- for _, param := range conf.Match.Param {
- scope = append(scope, model.ParamMatch{
- Key: ¶m.Key,
- Value:
model.StringMatchToModelStringMatch(param.Value),
- })
- }
-
- resp.FlowWeightSets =
append(resp.FlowWeightSets, model.FlowWeightSet{
- Weight: int32(weight),
- Scope: scope,
- })
- }
- return false
- })
+ resp, err := service.GetAppFlowWeight(ctx, appName, mesh)
if err != nil {
- c.JSON(http.StatusInternalServerError,
model.NewErrorResp(err.Error()))
+ util.HandleServiceError(c, err)
return
}
c.JSON(http.StatusOK, model.NewSuccessResp(resp))
}
}
-func isFlowWeight(conf *meshproto.OverrideConfig) bool {
- if conf.Side != consts.SideProvider ||
- conf.Parameters == nil ||
- conf.Match == nil ||
- conf.Match.Param == nil {
- return false
- } else if _, ok := conf.Parameters[`weight`]; !ok {
- return false
- }
- return true
-}
-
func ApplicationConfigFlowWeightPUT(ctx consolectx.Context) gin.HandlerFunc {
return func(c *gin.Context) {
- var (
- appName string
- mesh string
- body = struct {
- FlowWeightSets []model.FlowWeightSet
`json:"flowWeightSets"`
- }{}
- )
- appName = c.Query("appName")
- mesh = c.Query("mesh")
+ body := struct {
+ FlowWeightSets []model.FlowWeightSet
`json:"flowWeightSets"`
+ }{}
+ appName := c.Query("appName")
+ mesh := c.Query("mesh")
if strutil.IsBlank(appName) {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp("application name is required"))
+ util.HandleArgumentError(c, errors.New("appName is
required"))
+ return
}
if strutil.IsBlank(mesh) {
- c.JSON(http.StatusBadRequest, model.NewErrorResp("mesh
is required"))
+ util.HandleArgumentError(c, errors.New("mesh is
required"))
return
}
if err := c.Bind(&body); err != nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
+ util.HandleArgumentError(c, err)
return
}
- // get from store, or generate default resource
- isNotExist := false
- appConfiguratorName := appName + consts.ConfiguratorRuleSuffix
- res, err := service.GetConfigurator(ctx, appConfiguratorName,
mesh)
+ err := service.UpInsertAppFlowWeightConfig(ctx, appName, mesh,
body.FlowWeightSets)
if err != nil {
- if corestore.IsResourceNotFound(err) {
- // for check app exist
- data, err := service.GetApplicationDetail(ctx,
&model.ApplicationDetailReq{AppName: appName})
- if err != nil {
- c.JSON(http.StatusNotFound,
model.NewErrorResp(err.Error()))
- return
- } else if data == nil {
- c.JSON(http.StatusNotFound,
model.NewErrorResp("application not found"))
- return
- }
- res =
meshresource.NewDynamicConfigResourceWithAttributes(appConfiguratorName, mesh)
- res.Spec = &meshproto.DynamicConfig{
- Key: appName,
- Scope: consts.ScopeApplication,
- ConfigVersion:
consts.ConfiguratorVersionV3,
- Enabled: true,
- Configs:
make([]*meshproto.OverrideConfig, 0),
- }
- isNotExist = true
- } else {
- c.JSON(http.StatusInternalServerError,
model.NewErrorResp(err.Error()))
- return
- }
- }
-
- // remove old
- res.Spec.RangeConfigsToRemove(func(conf
*meshproto.OverrideConfig) (IsRemove bool) {
- return isFlowWeight(conf)
- })
- // append new
- for _, set := range body.FlowWeightSets {
- paramMatch := make([]*meshproto.ParamMatch, 0,
len(set.Scope))
- for _, match := range set.Scope {
- paramMatch = append(paramMatch,
&meshproto.ParamMatch{
- Key: *match.Key,
- Value:
model.ModelStringMatchToStringMatch(match.Value),
- })
- }
- res.Spec.Configs = append(res.Spec.Configs,
&meshproto.OverrideConfig{
- Side: consts.SideProvider,
- Parameters: map[string]string{`weight`:
strconv.Itoa(int(set.Weight))},
- Match: &meshproto.ConditionMatch{
- Param: paramMatch,
- },
- XGenerateByCp: true,
- })
- }
- // restore
- if isNotExist {
- err = service.CreateConfigurator(ctx, appName, res)
- if err != nil {
- c.JSON(http.StatusInternalServerError,
model.NewErrorResp(err.Error()))
- return
- }
- } else {
- err = service.UpdateConfigurator(ctx, appName, res)
- if err != nil {
- c.JSON(http.StatusInternalServerError,
model.NewErrorResp(err.Error()))
- return
- }
+ util.HandleServiceError(c, err)
+ return
}
- c.JSON(http.StatusOK, model.NewSuccessResp(nil))
+ c.JSON(http.StatusOK, model.NewSuccessResp(true))
}
}
func ApplicationConfigGrayGET(ctx consolectx.Context) gin.HandlerFunc {
return func(c *gin.Context) {
- var (
- appName string
- mesh string
- resp = struct {
- GraySets []model.GraySet `json:"graySets"`
- }{}
- )
- appName = c.Query("appName")
- mesh = c.Query("mesh")
+ appName := c.Query("appName")
+ mesh := c.Query("mesh")
if strutil.IsBlank(appName) {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp("appName is required"))
+ util.HandleArgumentError(c, errors.New("appName is
required"))
return
}
if strutil.IsBlank(mesh) {
- c.JSON(http.StatusBadRequest, model.NewErrorResp("mesh
is required"))
+ util.HandleArgumentError(c, errors.New("mesh is
required"))
return
}
- serviceTagRuleName := appName + consts.TagRuleSuffix
- res, err := service.GetTagRule(ctx, serviceTagRuleName, mesh)
+ resp, err := service.GetGrayConfig(ctx, appName, mesh)
if err != nil {
- if corestore.IsResourceNotFound(err) {
- resp.GraySets = make([]model.GraySet, 0)
- c.JSON(http.StatusOK,
model.NewSuccessResp(resp))
- return
- }
- c.JSON(http.StatusNotFound,
model.NewErrorResp(err.Error()))
+ util.HandleServiceError(c, err)
return
}
- resp.GraySets = make([]model.GraySet, 0, len(res.Spec.Tags))
-
- res.Spec.RangeTags(func(tag *meshproto.Tag) (isStop bool) {
- if isGrayTag(tag) {
- scope := make([]model.ParamMatch, 0,
len(tag.Match))
- for _, paramMatch := range tag.Match {
- scope = append(scope, model.ParamMatch{
- Key: ¶mMatch.Key,
- Value:
model.StringMatchToModelStringMatch(paramMatch.Value),
- })
- }
- resp.GraySets = append(resp.GraySets,
model.GraySet{
- EnvName: tag.Name,
- Scope: scope,
- })
- }
- return false
- })
-
c.JSON(http.StatusOK, model.NewSuccessResp(resp))
}
}
func ApplicationConfigGrayPUT(ctx consolectx.Context) gin.HandlerFunc {
return func(c *gin.Context) {
- var (
- appName string
- mesh string
- body = struct {
- GraySets []model.GraySet `json:"graySets"`
- }{}
- )
- appName = c.Query("appName")
- mesh = c.Query("mesh")
+ body := struct {
+ GraySets []model.GraySet `json:"graySets"`
+ }{}
+ appName := c.Query("appName")
+ mesh := c.Query("mesh")
if strutil.IsBlank(appName) {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp("application name is required"))
+ util.HandleArgumentError(c, errors.New("appName is
required"))
return
}
if strutil.IsBlank(mesh) {
- c.JSON(http.StatusBadRequest, model.NewErrorResp("mesh
is required"))
+ util.HandleArgumentError(c, errors.New("mesh is
required"))
return
}
if err := c.Bind(&body); err != nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
+ util.HandleArgumentError(c, err)
+ return
}
-
- isNotExist := false
- serviceTagRuleName := appName + consts.TagRuleSuffix
- res, err := service.GetTagRule(ctx, serviceTagRuleName, mesh)
+ err := service.UpInsertAppGrayConfig(ctx, appName, mesh,
body.GraySets)
if err != nil {
- c.JSON(http.StatusInternalServerError,
model.NewErrorResp(err.Error()))
- }
- if res == nil {
- data, err := service.GetApplicationDetail(ctx,
&model.ApplicationDetailReq{AppName: appName, Mesh: mesh})
- if err != nil {
- c.JSON(http.StatusNotFound,
model.NewErrorResp(err.Error()))
- return
- } else if data == nil {
- c.JSON(http.StatusNotFound,
model.NewErrorResp("application not found"))
- return
- }
-
- res =
meshresource.NewTagRouteResourceWithAttributes(serviceTagRuleName, mesh)
- res.Spec = &meshproto.TagRoute{
- Enabled: true,
- Key: appName,
- ConfigVersion: consts.ConfiguratorVersionV3,
- Force: false,
- Tags: make([]*meshproto.Tag, 0),
- }
- isNotExist = true
- }
-
- // remove old config, generate config from admin, append
- res.Spec.RangeTagsToRemove(func(tag *meshproto.Tag) (IsRemove
bool) {
- return isGrayTag(tag)
- })
- newTags := make([]*meshproto.Tag, 0)
- for _, set := range body.GraySets {
- paramMatches := make([]*meshproto.ParamMatch, 0,
len(set.Scope))
- for _, match := range set.Scope {
- paramMatches = append(paramMatches,
&meshproto.ParamMatch{
- Key: *match.Key,
- Value:
model.ModelStringMatchToStringMatch(match.Value),
- })
- }
- newTags = append(newTags, &meshproto.Tag{
- Name: set.EnvName,
- Match: paramMatches,
- XGenerateByCp: true,
- })
- }
- res.Spec.Tags = append(res.Spec.Tags, newTags...)
-
- // restore
- if isNotExist {
- err = service.CreateTagRule(ctx, res)
- if err != nil {
- c.JSON(http.StatusInternalServerError,
model.NewErrorResp(err.Error()))
- return
- }
- } else {
- err = service.UpdateTagRule(ctx, res)
- if err != nil {
- c.JSON(http.StatusInternalServerError,
model.NewErrorResp(err.Error()))
- return
- }
+ util.HandleServiceError(c, err)
+ return
}
- c.JSON(http.StatusOK, model.NewSuccessResp(nil))
- }
-}
-
-func isGrayTag(tag *meshproto.Tag) bool {
- if tag.Name == "" || tag.Addresses != nil || len(tag.Addresses) != 0 {
- return false
+ c.JSON(http.StatusOK, model.NewSuccessResp(true))
}
- return true
}
diff --git a/pkg/console/handler/auth.go b/pkg/console/handler/auth.go
index dc1777e4..0ec4dcf7 100644
--- a/pkg/console/handler/auth.go
+++ b/pkg/console/handler/auth.go
@@ -23,6 +23,7 @@ import (
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
+ "github.com/apache/dubbo-admin/pkg/common/bizerror"
consolectx "github.com/apache/dubbo-admin/pkg/console/context"
"github.com/apache/dubbo-admin/pkg/console/model"
)
@@ -33,21 +34,24 @@ func Login(ctx consolectx.Context) gin.HandlerFunc {
password := c.PostForm("password")
// verify username and password
authCfg := ctx.Config().Console.Auth
- if user == authCfg.User && password == authCfg.Password {
- session := sessions.Default(c)
- session.Set("user", user)
- session.Options(sessions.Options{
- MaxAge: authCfg.ExpirationTime,
- Path: "/",
- })
- err := session.Save()
- if err != nil {
- c.JSON(http.StatusInternalServerError,
model.NewErrorResp(err.Error()))
- }
- c.JSON(http.StatusOK, model.NewSuccessResp(nil))
- } else {
- c.JSON(http.StatusUnauthorized,
model.NewUnauthorizedResp())
+ if user != authCfg.User || password != authCfg.Password {
+ authErr := bizerror.NewBizError(bizerror.Unauthorized,
"username or password is not correct!")
+ c.JSON(http.StatusUnauthorized,
model.NewBizErrorResp(authErr))
+ return
}
+ session := sessions.Default(c)
+ session.Set("user", user)
+ session.Options(sessions.Options{
+ MaxAge: authCfg.ExpirationTime,
+ Path: "/",
+ })
+ err := session.Save()
+ if err != nil {
+ sessionErr :=
bizerror.NewBizError(bizerror.SessionError, err.Error())
+ c.JSON(http.StatusOK, model.NewBizErrorResp(sessionErr))
+ return
+ }
+ c.JSON(http.StatusOK, model.NewSuccessResp(true))
}
}
@@ -57,8 +61,10 @@ func Logout(_ consolectx.Context) gin.HandlerFunc {
session.Clear()
err := session.Save()
if err != nil {
- c.JSON(http.StatusInternalServerError,
model.NewErrorResp(err.Error()))
+ sessionErr :=
bizerror.NewBizError(bizerror.SessionError, err.Error())
+ c.JSON(http.StatusOK, model.NewBizErrorResp(sessionErr))
+ return
}
- c.JSON(http.StatusOK, model.NewSuccessResp(nil))
+ c.JSON(http.StatusOK, model.NewSuccessResp(true))
}
}
diff --git a/pkg/common/bizerror/common.go b/pkg/console/handler/mesh.go
similarity index 53%
copy from pkg/common/bizerror/common.go
copy to pkg/console/handler/mesh.go
index 1f993c4b..3ebee0f9 100644
--- a/pkg/common/bizerror/common.go
+++ b/pkg/console/handler/mesh.go
@@ -15,41 +15,29 @@
* limitations under the License.
*/
-package bizerror
+package handler
import (
- "errors"
- "fmt"
-)
-
-type AssertionError struct {
- msg string
-}
-
-func NewAssertionError(expected, actual interface{}) error {
- return &AssertionError{
- msg: fmt.Sprintf("type assertion error, expected:%v,
actual:%v", expected, actual),
- }
-}
-
-func (e *AssertionError) Error() string {
- return e.msg
-}
-
-type MeshNotFoundError struct {
- Mesh string
-}
+ "net/http"
-func (m *MeshNotFoundError) Error() string {
- return fmt.Sprintf("mesh of name %s is not found", m.Mesh)
-}
+ "github.com/duke-git/lancet/v2/slice"
+ "github.com/gin-gonic/gin"
-func MeshNotFound(meshName string) error {
- return &MeshNotFoundError{meshName}
-}
+ discoverycfg "github.com/apache/dubbo-admin/pkg/config/discovery"
+ consolectx "github.com/apache/dubbo-admin/pkg/console/context"
+ "github.com/apache/dubbo-admin/pkg/console/model"
+)
-func IsMeshNotFound(err error) bool {
- var meshNotFoundError *MeshNotFoundError
- ok := errors.As(err, &meshNotFoundError)
- return ok
+// ListMeshes list all meshes(discoveries) defined in config
+func ListMeshes(ctx consolectx.Context) gin.HandlerFunc {
+ return func(c *gin.Context) {
+ discoveries := ctx.Config().Discovery
+ meshes := slice.Map(discoveries, func(index int, item
*discoverycfg.Config) model.MeshResp {
+ return model.MeshResp{
+ Name: item.Name,
+ Type: string(item.Type),
+ }
+ })
+ c.JSON(http.StatusOK, model.NewSuccessResp(meshes))
+ }
}
diff --git a/pkg/console/model/application.go b/pkg/console/model/application.go
index 3720de36..5b02b674 100644
--- a/pkg/console/model/application.go
+++ b/pkg/console/model/application.go
@@ -195,3 +195,15 @@ type GraySet struct {
EnvName string `json:"name,omitempty"`
Scope []ParamMatch `json:"scope,omitempty"`
}
+
+type AppAccessLogConfigResp struct {
+ AccessLog bool `json:"operatorLog"`
+}
+
+type AppFlowWeightConfigResp struct {
+ FlowWeightSets []FlowWeightSet `json:"flowWeightSets"`
+}
+
+type AppGrayConfigResp struct {
+ GraySets []GraySet `json:"graySets"`
+}
diff --git a/pkg/console/model/common.go b/pkg/console/model/common.go
index 7a11ea86..18ab50b3 100644
--- a/pkg/console/model/common.go
+++ b/pkg/console/model/common.go
@@ -17,21 +17,18 @@
package model
-import coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model"
-
-const (
- successCode = 200
- unauthorizedCode = 401
- errorCode = 500
+import (
+ "github.com/apache/dubbo-admin/pkg/common/bizerror"
+ coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model"
)
type CommonResp struct {
- Code int `json:"code"`
+ Code string `json:"code"`
Msg string `json:"msg"`
Data any `json:"data"`
}
-func (r *CommonResp) WithCode(code int) *CommonResp {
+func (r *CommonResp) WithCode(code string) *CommonResp {
r.Code = code
return r
}
@@ -48,24 +45,25 @@ func (r *CommonResp) WithData(data any) *CommonResp {
func NewSuccessResp(data any) *CommonResp {
return &CommonResp{
- Code: successCode,
+ Code: "Success",
Msg: "success",
Data: data,
}
}
-func NewUnauthorizedResp() *CommonResp {
+// NewErrorResp TODO replace with NewBizErrorResp
+func NewErrorResp(msg string) *CommonResp {
return &CommonResp{
- Code: unauthorizedCode,
- Msg: "UnAuthorized, please login",
+ Code: string(bizerror.UnknownError),
+ Msg: msg,
Data: nil,
}
}
-func NewErrorResp(msg string) *CommonResp {
+func NewBizErrorResp(err bizerror.Error) *CommonResp {
return &CommonResp{
- Code: errorCode,
- Msg: msg,
+ Code: string(err.Code()),
+ Msg: err.Message(),
Data: nil,
}
}
diff --git a/pkg/console/model/condition_rule.go
b/pkg/console/model/condition_rule.go
index e13d7e2c..42bb6bc6 100644
--- a/pkg/console/model/condition_rule.go
+++ b/pkg/console/model/condition_rule.go
@@ -18,7 +18,6 @@
package model
import (
- "net/http"
"strings"
meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1"
@@ -209,11 +208,7 @@ func matchValueToDestinationCondition(val string)
[]DestinationCondition {
func GenConditionRuleToResp(data *meshproto.ConditionRoute) *CommonResp {
if data == nil {
- return &CommonResp{
- Code: http.StatusNotFound,
- Msg: "not found",
- Data: map[string]string{},
- }
+ return NewSuccessResp(nil)
}
if pb := data.ToConditionRouteV3(); pb != nil {
return NewSuccessResp(ConditionRuleResp{
@@ -249,11 +244,7 @@ func GenConditionRuleToResp(data
*meshproto.ConditionRoute) *CommonResp {
}
return NewSuccessResp(res)
} else {
- return &CommonResp{
- Code: http.StatusInternalServerError,
- Msg: "invalid condition rule",
- Data: data,
- }
+ return NewErrorResp("invalid condition rule")
}
}
diff --git a/pkg/console/model/configurator_rule.go
b/pkg/console/model/configurator_rule.go
index facced62..677f742e 100644
--- a/pkg/console/model/configurator_rule.go
+++ b/pkg/console/model/configurator_rule.go
@@ -18,8 +18,6 @@
package model
import (
- "net/http"
-
meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1"
coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model"
)
@@ -101,7 +99,7 @@ type RespAddressMatch struct {
}
func GenDynamicConfigToResp(pb *meshproto.DynamicConfig) (res *CommonResp) {
- cfg := RespConfigurator{}
+ cfg := &RespConfigurator{}
if pb != nil {
cfg.ConfigVersion = pb.ConfigVersion
cfg.Key = pb.Key
@@ -110,10 +108,7 @@ func GenDynamicConfigToResp(pb *meshproto.DynamicConfig)
(res *CommonResp) {
cfg.Configs = overrideConfigToRespConfigItem(pb.Configs)
return NewSuccessResp(cfg)
}
- return &CommonResp{
- Code: http.StatusNotFound,
- Msg: "configurator not found",
- }
+ return NewSuccessResp(nil)
}
func overrideConfigToRespConfigItem(OverrideConfigs
[]*meshproto.OverrideConfig) []ConfigItem {
diff --git a/pkg/common/bizerror/common.go b/pkg/console/model/mesh.go
similarity index 53%
copy from pkg/common/bizerror/common.go
copy to pkg/console/model/mesh.go
index 1f993c4b..7ee9e538 100644
--- a/pkg/common/bizerror/common.go
+++ b/pkg/console/model/mesh.go
@@ -15,41 +15,9 @@
* limitations under the License.
*/
-package bizerror
+package model
-import (
- "errors"
- "fmt"
-)
-
-type AssertionError struct {
- msg string
-}
-
-func NewAssertionError(expected, actual interface{}) error {
- return &AssertionError{
- msg: fmt.Sprintf("type assertion error, expected:%v,
actual:%v", expected, actual),
- }
-}
-
-func (e *AssertionError) Error() string {
- return e.msg
-}
-
-type MeshNotFoundError struct {
- Mesh string
-}
-
-func (m *MeshNotFoundError) Error() string {
- return fmt.Sprintf("mesh of name %s is not found", m.Mesh)
-}
-
-func MeshNotFound(meshName string) error {
- return &MeshNotFoundError{meshName}
-}
-
-func IsMeshNotFound(err error) bool {
- var meshNotFoundError *MeshNotFoundError
- ok := errors.As(err, &meshNotFoundError)
- return ok
+type MeshResp struct {
+ Name string `json:"name"`
+ Type string `json:"type"`
}
diff --git a/pkg/console/model/tag_rule.go b/pkg/console/model/tag_rule.go
index 7bdf1ecd..6890ef1f 100644
--- a/pkg/console/model/tag_rule.go
+++ b/pkg/console/model/tag_rule.go
@@ -18,8 +18,6 @@
package model
import (
- "net/http"
-
meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1"
"github.com/apache/dubbo-admin/pkg/core/consts"
)
@@ -47,11 +45,7 @@ type RespTagElement struct {
func GenTagRouteResp(pb *meshproto.TagRoute) *CommonResp {
if pb == nil {
- return &CommonResp{
- Code: http.StatusNotFound,
- Msg: "tag rule not found",
- Data: "",
- }
+ return NewSuccessResp(nil)
} else {
return NewSuccessResp(TagRuleResp{
ConfigVersion: pb.ConfigVersion,
diff --git a/pkg/console/router/router.go b/pkg/console/router/router.go
index 334f9fee..f8370699 100644
--- a/pkg/console/router/router.go
+++ b/pkg/console/router/router.go
@@ -67,8 +67,8 @@ func InitRouter(r *gin.Engine, ctx consolectx.Context) {
application.GET("/search", handler.ApplicationSearch(ctx))
{
applicationConfig := application.Group("/config")
- applicationConfig.PUT("/operatorLog",
handler.ApplicationConfigOperatorLogPut(ctx))
- applicationConfig.GET("/operatorLog",
handler.ApplicationConfigOperatorLogGet(ctx))
+ applicationConfig.PUT("/operatorLog",
handler.ApplicationConfigAccessLogPut(ctx))
+ applicationConfig.GET("/operatorLog",
handler.ApplicationConfigAccessLogGet(ctx))
applicationConfig.GET("/flowWeight",
handler.ApplicationConfigFlowWeightGET(ctx))
applicationConfig.PUT("/flowWeight",
handler.ApplicationConfigFlowWeightPUT(ctx))
@@ -139,4 +139,5 @@ func InitRouter(r *gin.Engine, ctx consolectx.Context) {
router.GET("/search", handler.BannerGlobalSearch(ctx))
router.GET("/overview", handler.ClusterOverview(ctx))
router.GET("/metadata", handler.AdminMetadata(ctx))
+ router.GET("/meshes", handler.ListMeshes(ctx))
}
diff --git a/pkg/console/service/application.go
b/pkg/console/service/application.go
index d78f512a..836c7019 100644
--- a/pkg/console/service/application.go
+++ b/pkg/console/service/application.go
@@ -18,13 +18,19 @@
package service
import (
+ "fmt"
+ "strconv"
+
"github.com/duke-git/lancet/v2/maputil"
"github.com/duke-git/lancet/v2/slice"
"github.com/duke-git/lancet/v2/strutil"
+ meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1"
+ "github.com/apache/dubbo-admin/pkg/common/bizerror"
consolectx "github.com/apache/dubbo-admin/pkg/console/context"
"github.com/apache/dubbo-admin/pkg/console/model"
"github.com/apache/dubbo-admin/pkg/core/consts"
+ "github.com/apache/dubbo-admin/pkg/core/logger"
"github.com/apache/dubbo-admin/pkg/core/manager"
meshresource
"github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1"
coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model"
@@ -267,3 +273,405 @@ func buildApplicationSearchResp(appResource
*meshresource.ApplicationResource, m
RegistryClusters: []string{mesh},
}
}
+
+func isAppAccessLogConfig(conf *meshproto.OverrideConfig, appName string) bool
{
+ if conf.Side != consts.SideProvider ||
+ conf.Parameters == nil ||
+ conf.Match == nil ||
+ conf.Match.Application == nil ||
+ conf.Match.Application.Oneof == nil ||
+ len(conf.Match.Application.Oneof) != 1 ||
+ conf.Match.Application.Oneof[0].Exact != appName {
+ return false
+ }
+ if _, ok := conf.Parameters[`accesslog`]; !ok {
+ return false
+ }
+ return true
+}
+
+func UpInsertAppAccessLog(ctx consolectx.Context, appName string,
openAccessLog bool, mesh string) error {
+ // check app exists
+ data, err := GetApplicationDetail(ctx,
&model.ApplicationDetailReq{AppName: appName})
+ if err != nil {
+ return err
+ }
+ if data == nil {
+ return bizerror.NewBizError(bizerror.AppNotFound,
fmt.Sprintf("%s does not exist", appName))
+ }
+ // check app configurator exists
+ appConfiguratorName := appName + consts.ConfiguratorRuleSuffix
+ res, err := GetConfigurator(ctx, appConfiguratorName, mesh)
+ if err != nil {
+ return err
+ }
+ // if not exists, create one configurator with access log enable
+ if res == nil {
+ return insertConfiguratorWithAccessLog(ctx, res, openAccessLog,
appConfiguratorName, appName, mesh)
+ }
+ // else we update the configurator
+ return updateConfiguratorWithAccessLog(ctx, res, openAccessLog,
appConfiguratorName, appName, mesh)
+}
+
+func insertConfiguratorWithAccessLog(ctx consolectx.Context, res
*meshresource.DynamicConfigResource, openAccessLog bool,
+ appConfiguratorName, appName, mesh string) error {
+ // configurator is nil, accessLog is already closed
+ if !openAccessLog {
+ return nil
+ }
+ res =
meshresource.NewDynamicConfigResourceWithAttributes(appConfiguratorName, mesh)
+ res.Spec = &meshproto.DynamicConfig{
+ Key: appName,
+ Scope: consts.ScopeApplication,
+ ConfigVersion: consts.ConfiguratorVersionV3,
+ Enabled: true,
+ Configs: make([]*meshproto.OverrideConfig, 0),
+ }
+ res.Spec.Configs = append(res.Spec.Configs,
newAccessLogEnabledConfig(appName))
+ err := CreateConfigurator(ctx, appConfiguratorName, res)
+ if err != nil {
+ logger.Errorf("create configurator failed when open accesslog,
resourceKey: %s, openAccessLog: %t, err: %s",
+ coremodel.BuildResourceKey(mesh, appName),
openAccessLog, err)
+ return err
+ }
+ return nil
+}
+
+func updateConfiguratorWithAccessLog(ctx consolectx.Context, res
*meshresource.DynamicConfigResource, openAccessLog bool,
+ appConfiguratorName, appName, mesh string) error {
+ var accessLogConfig *meshproto.OverrideConfig
+ res.Spec.RangeConfig(func(conf *meshproto.OverrideConfig) (isStop bool)
{
+ if isAppAccessLogConfig(conf, appName) {
+ accessLogConfig = conf
+ return true
+ }
+ return false
+ })
+ // access log config not found
+ if accessLogConfig == nil {
+ // access log needs to be closed and already closed
+ if !openAccessLog {
+ return nil
+ }
+ // insert a access log enabled config
+ res.Spec.Configs = append(res.Spec.Configs,
newAccessLogEnabledConfig(appName))
+ } else {
+ // access log config found and status is the same as needed
+ if accessLogConfig.Enabled == openAccessLog {
+ return nil
+ }
+ // update the access log enabled status as needed
+ accessLogConfig.Enabled = openAccessLog
+ }
+ err := UpdateConfigurator(ctx, appConfiguratorName, res)
+ if err != nil {
+ logger.Errorf("update configurator failed when opening
accesslog, resourceKey: %s, openAccessLog: %t, err: %s",
+ coremodel.BuildResourceKey(mesh, appName),
openAccessLog, err)
+ return err
+ }
+ return nil
+}
+
+func newAccessLogEnabledConfig(appName string) *meshproto.OverrideConfig {
+ return &meshproto.OverrideConfig{
+ Side: consts.SideProvider,
+ Parameters: map[string]string{`accesslog`: `true`},
+ Enabled: true,
+ Match: &meshproto.ConditionMatch{
+ Application: &meshproto.ListStringMatch{
+ Oneof: []*meshproto.StringMatch{
+ {
+ Exact: appName,
+ },
+ }}},
+ XGenerateByCp: true,
+ }
+}
+
+func GetAppAccessLog(ctx consolectx.Context, appName string, mesh string)
(*model.AppAccessLogConfigResp, error) {
+ appConfiguratorName := appName + consts.ConfiguratorRuleSuffix
+ res, err := GetConfigurator(ctx, appConfiguratorName, mesh)
+ resp := &model.AppAccessLogConfigResp{
+ AccessLog: false,
+ }
+ if err != nil {
+ logger.Errorf("get configurator failed when get app accesslog,
resourceKey: %s, err: %s",
+ coremodel.BuildResourceKey(mesh, appName), err)
+ return nil, err
+ }
+ if res == nil {
+ return resp, nil
+ }
+ var appAccessLogConfig *meshproto.OverrideConfig
+ res.Spec.RangeConfig(func(conf *meshproto.OverrideConfig) (isStop bool)
{
+ if isAppAccessLogConfig(conf, appName) {
+ appAccessLogConfig = conf
+ return true
+ }
+ return false
+ })
+ if appAccessLogConfig == nil {
+ return resp, nil
+ }
+ resp.AccessLog = appAccessLogConfig.Enabled
+ return resp, nil
+}
+
+func GetAppFlowWeight(ctx consolectx.Context, appName string, mesh string)
(*model.AppFlowWeightConfigResp, error) {
+ resp := &model.AppFlowWeightConfigResp{
+ FlowWeightSets: []model.FlowWeightSet{},
+ }
+ appConfiguratorName := appName + consts.ConfiguratorRuleSuffix
+ res, err := GetConfigurator(ctx, appConfiguratorName, mesh)
+ if err != nil {
+ logger.Errorf("get configurator failed when get app flow
weight, resourceKey: %s, err: %s",
+ coremodel.BuildResourceKey(mesh, appName), err)
+ return nil, err
+ }
+ if res == nil {
+ return resp, nil
+ }
+
+ weight := 0
+ res.Spec.RangeConfig(func(conf *meshproto.OverrideConfig) (isStop bool)
{
+ if isFlowWeightConfig(conf) {
+ weight, err = strconv.Atoi(conf.Parameters[`weight`])
+ if err != nil {
+ logger.Error("parse weight failed", err)
+ return true
+ }
+ scope := make([]model.ParamMatch, 0,
len(conf.Match.Param))
+ for _, param := range conf.Match.Param {
+ scope = append(scope, model.ParamMatch{
+ Key: ¶m.Key,
+ Value:
model.StringMatchToModelStringMatch(param.Value),
+ })
+ }
+
+ resp.FlowWeightSets = append(resp.FlowWeightSets,
model.FlowWeightSet{
+ Weight: int32(weight),
+ Scope: scope,
+ })
+ }
+ return false
+ })
+ return resp, nil
+}
+
+func isFlowWeightConfig(conf *meshproto.OverrideConfig) bool {
+ if conf.Side != consts.SideProvider ||
+ conf.Parameters == nil ||
+ conf.Match == nil ||
+ conf.Match.Param == nil {
+ return false
+ }
+ if _, ok := conf.Parameters[`weight`]; !ok {
+ return false
+ }
+ return true
+}
+
+func UpInsertAppFlowWeightConfig(ctx consolectx.Context, appName string, mesh
string, flowWeightSets []model.FlowWeightSet) error {
+ // check app exists
+ data, err := GetApplicationDetail(ctx,
&model.ApplicationDetailReq{AppName: appName})
+ if err != nil {
+ return err
+ }
+ if data == nil {
+ return bizerror.NewBizError(bizerror.AppNotFound,
fmt.Sprintf("%s does not exist", appName))
+ }
+ appConfiguratorName := appName + consts.ConfiguratorRuleSuffix
+ res, err := GetConfigurator(ctx, appConfiguratorName, mesh)
+ if err != nil {
+ logger.Errorf("get configurator failed when update app flow
weight, resourceKey: %s, err: %s",
+ coremodel.BuildResourceKey(mesh, appName), err)
+ return err
+ }
+ // configurator not exists, insert a new one
+ if res == nil {
+ return insertConfiguratorWithFlowWeight(ctx, flowWeightSets,
appName, appConfiguratorName, mesh)
+ }
+ // configurator exists, update it
+
+ // remove old flow weight config
+ res.Spec.RangeConfigsToRemove(func(conf *meshproto.OverrideConfig)
(IsRemove bool) {
+ return isFlowWeightConfig(conf)
+ })
+
+ // add new flow weight config
+ flowWeightConfigs := slice.Map(flowWeightSets, func(index int, set
model.FlowWeightSet) *meshproto.OverrideConfig {
+ return fromFlowWeightSet(set)
+ })
+ res.Spec.Configs = slice.Union(res.Spec.Configs, flowWeightConfigs)
+
+ err = UpdateConfigurator(ctx, appConfiguratorName, res)
+ if err != nil {
+ logger.Errorf("update configurator failed with app flow weight,
resourceKey: %s, err: %s",
+ coremodel.BuildResourceKey(mesh, appName), err)
+ return err
+ }
+ return nil
+}
+
+func insertConfiguratorWithFlowWeight(
+ ctx consolectx.Context,
+ flowWeightSets []model.FlowWeightSet,
+ appName, appConfiguratorName, mesh string) error {
+ res :=
meshresource.NewDynamicConfigResourceWithAttributes(appConfiguratorName, mesh)
+ res.Spec = &meshproto.DynamicConfig{
+ Key: appName,
+ Scope: consts.ScopeApplication,
+ ConfigVersion: consts.ConfiguratorVersionV3,
+ Enabled: true,
+ }
+ flowWeightConfigs := slice.Map(flowWeightSets, func(index int, set
model.FlowWeightSet) *meshproto.OverrideConfig {
+ return fromFlowWeightSet(set)
+ })
+ res.Spec.Configs = flowWeightConfigs
+ err := CreateConfigurator(ctx, appConfiguratorName, res)
+ if err != nil {
+ logger.Errorf("insert configurator failed with app flow weight,
resourceKey: %s, err: %s",
+ coremodel.BuildResourceKey(mesh, appName), err)
+ return err
+ }
+ return nil
+}
+
+func fromFlowWeightSet(set model.FlowWeightSet) *meshproto.OverrideConfig {
+ paramMatch := make([]*meshproto.ParamMatch, 0, len(set.Scope))
+ for _, match := range set.Scope {
+ paramMatch = append(paramMatch, &meshproto.ParamMatch{
+ Key: *match.Key,
+ Value: model.ModelStringMatchToStringMatch(match.Value),
+ })
+ }
+ return &meshproto.OverrideConfig{
+ Side: consts.SideProvider,
+ Parameters: map[string]string{`weight`:
strconv.Itoa(int(set.Weight))},
+ Match: &meshproto.ConditionMatch{
+ Param: paramMatch,
+ },
+ XGenerateByCp: true,
+ }
+}
+func GetGrayConfig(ctx consolectx.Context, appName string, mesh string)
(*model.AppGrayConfigResp, error) {
+ resp := &model.AppGrayConfigResp{}
+ serviceTagRuleName := appName + consts.TagRuleSuffix
+ res, err := GetTagRule(ctx, serviceTagRuleName, mesh)
+ if err != nil {
+ logger.Errorf("get tag rule failed when get gray config,
resourceKey: %s, err: %s",
+ coremodel.BuildResourceKey(mesh, appName), err)
+ return nil, err
+ }
+ if res == nil {
+ return resp, err
+ }
+ resp.GraySets = make([]model.GraySet, 0, len(res.Spec.Tags))
+
+ res.Spec.RangeTags(func(tag *meshproto.Tag) (isStop bool) {
+ if isGrayTag(tag) {
+ scope := make([]model.ParamMatch, 0, len(tag.Match))
+ for _, paramMatch := range tag.Match {
+ scope = append(scope, model.ParamMatch{
+ Key: ¶mMatch.Key,
+ Value:
model.StringMatchToModelStringMatch(paramMatch.Value),
+ })
+ }
+ resp.GraySets = append(resp.GraySets, model.GraySet{
+ EnvName: tag.Name,
+ Scope: scope,
+ })
+ }
+ return false
+ })
+ return resp, nil
+}
+
+func isGrayTag(tag *meshproto.Tag) bool {
+ if tag.Name == "" || len(tag.Addresses) != 0 {
+ return false
+ }
+ return true
+}
+
+func UpInsertAppGrayConfig(ctx consolectx.Context, appName string, mesh
string, graySets []model.GraySet) error {
+ serviceTagRuleName := appName + consts.TagRuleSuffix
+ res, err := GetTagRule(ctx, serviceTagRuleName, mesh)
+ if err != nil {
+ logger.Errorf("get tag rule failed when update app gray config,
resourceKey: %s, err: %s",
+ coremodel.BuildResourceKey(mesh, appName), err)
+ return err
+ }
+ _, err = GetApplicationDetail(ctx, &model.ApplicationDetailReq{AppName:
appName, Mesh: mesh})
+ if err != nil {
+ return err
+ }
+ // tag rule not exists, insert a new one
+ if res == nil {
+ return insertTagRuleWithGrayConfig(ctx, graySets, appName,
serviceTagRuleName, mesh)
+ }
+ // tag rule exists, update it
+ return updateTagRuleWithGrayConfig(ctx, graySets, res, appName, mesh)
+}
+
+func insertTagRuleWithGrayConfig(
+ ctx consolectx.Context,
+ graySets []model.GraySet,
+ appName, serviceTagRuleName, mesh string) error {
+ res :=
meshresource.NewTagRouteResourceWithAttributes(serviceTagRuleName, mesh)
+ res.Spec = &meshproto.TagRoute{
+ Enabled: true,
+ Key: appName,
+ ConfigVersion: consts.ConfiguratorVersionV3,
+ Force: false,
+ }
+ tags := slice.Map(graySets, func(index int, set model.GraySet)
*meshproto.Tag {
+ return fromGraySet(set)
+ })
+ res.Spec.Tags = tags
+ err := CreateTagRule(ctx, res)
+ if err != nil {
+ logger.Errorf("insert tag rule failed with app gray config,
resourceKey: %s, err: %s",
+ coremodel.BuildResourceKey(mesh, appName), err)
+ return err
+ }
+ return nil
+}
+
+func updateTagRuleWithGrayConfig(
+ ctx consolectx.Context,
+ graySets []model.GraySet,
+ res *meshresource.TagRouteResource,
+ appName string, mesh string) error {
+ // remove old config, generate config from admin, append
+ res.Spec.RangeTagsToRemove(func(tag *meshproto.Tag) (IsRemove bool) {
+ return isGrayTag(tag)
+ })
+ tags := slice.Map(graySets, func(index int, set model.GraySet)
*meshproto.Tag {
+ return fromGraySet(set)
+ })
+ res.Spec.Tags = append(res.Spec.Tags, tags...)
+ err := UpdateTagRule(ctx, res)
+ if err != nil {
+ logger.Errorf("update tag rule failed with app gray config,
resourceKey: %s, err: %s",
+ coremodel.BuildResourceKey(mesh, appName), err)
+ return err
+ }
+ return nil
+}
+
+func fromGraySet(set model.GraySet) *meshproto.Tag {
+ paramMatches := make([]*meshproto.ParamMatch, 0, len(set.Scope))
+ for _, match := range set.Scope {
+ paramMatches = append(paramMatches, &meshproto.ParamMatch{
+ Key: *match.Key,
+ Value: model.ModelStringMatchToStringMatch(match.Value),
+ })
+ }
+
+ return &meshproto.Tag{
+ Name: set.EnvName,
+ Match: paramMatches,
+ XGenerateByCp: true,
+ }
+}
diff --git a/pkg/common/bizerror/common.go b/pkg/console/util/error.go
similarity index 55%
copy from pkg/common/bizerror/common.go
copy to pkg/console/util/error.go
index 1f993c4b..62eb3287 100644
--- a/pkg/common/bizerror/common.go
+++ b/pkg/console/util/error.go
@@ -15,41 +15,27 @@
* limitations under the License.
*/
-package bizerror
+package util
import (
"errors"
- "fmt"
-)
-
-type AssertionError struct {
- msg string
-}
-
-func NewAssertionError(expected, actual interface{}) error {
- return &AssertionError{
- msg: fmt.Sprintf("type assertion error, expected:%v,
actual:%v", expected, actual),
- }
-}
+ "net/http"
-func (e *AssertionError) Error() string {
- return e.msg
-}
-
-type MeshNotFoundError struct {
- Mesh string
-}
+ "github.com/gin-gonic/gin"
-func (m *MeshNotFoundError) Error() string {
- return fmt.Sprintf("mesh of name %s is not found", m.Mesh)
-}
+ "github.com/apache/dubbo-admin/pkg/common/bizerror"
+ "github.com/apache/dubbo-admin/pkg/console/model"
+)
-func MeshNotFound(meshName string) error {
- return &MeshNotFoundError{meshName}
+func HandleServiceError(ctx *gin.Context, err error) {
+ var e bizerror.Error
+ if !errors.As(err, &e) {
+ e = bizerror.NewBizError(bizerror.UnknownError, err.Error())
+ }
+ ctx.JSON(http.StatusOK, model.NewBizErrorResp(e))
}
-func IsMeshNotFound(err error) bool {
- var meshNotFoundError *MeshNotFoundError
- ok := errors.As(err, &meshNotFoundError)
- return ok
+func HandleArgumentError(ctx *gin.Context, err error) {
+ e := bizerror.NewBizError(bizerror.InvalidArgument, err.Error())
+ ctx.JSON(http.StatusOK, model.NewBizErrorResp(e))
}