Copilot commented on code in PR #1463:
URL: https://github.com/apache/dubbo-admin/pull/1463#discussion_r3143422736
##########
pkg/console/model/application.go:
##########
@@ -88,68 +88,49 @@ func NewApplicationDetail() *ApplicationDetail {
Workloads: NewSet(),
}
}
-
-func (a *ApplicationDetail) MergeMetaData(metadata *mesh.MetaDataResource) {
- a.mergeServiceInfo(metadata)
-}
-
-func (a *ApplicationDetail) mergeServiceInfo(metadata *mesh.MetaDataResource) {
- for _, serviceInfo := range metadata.Spec.Services {
- a.DubboVersions.Add(fmt.Sprintf("dubbo %s",
serviceInfo.Params[constants.ReleaseKey]))
- a.RPCProtocols.Add(serviceInfo.Protocol)
-
a.SerialProtocols.Add(serviceInfo.Params[constants.SerializationKey])
-
- }
-}
-
-func (a *ApplicationDetail) MergeDataplane(dataplane *mesh.DataplaneResource) {
- if work, ok := dataplane.Spec.Extensions[constants.WorkLoadKey]; ok &&
- regexp.MustCompile(`^.*-\d+$`).MatchString(work) {
+func (a *ApplicationDetail) MergeInstance(instanceRes
*meshresource.InstanceResource, cfg app.AdminConfig) {
+ instance := instanceRes.Spec
+ if instance.WorkloadType == constants.StatefulSet {
a.AppTypes.Add(constants.Stateful)
} else {
a.AppTypes.Add(constants.Stateless)
}
-
- inbounds := dataplane.Spec.Networking.Inbound
- for _, inbound := range inbounds {
- a.mergeInbound(inbound)
+ a.DubboPorts.Add(strconv.FormatInt(instance.RpcPort, 10))
+ a.DubboVersions.Add(instance.ReleaseVersion)
+ a.Images.Add(instance.Image)
+ if d := cfg.FindDiscovery(instanceRes.Mesh); d != nil {
+ a.RegisterClusters.Add(instanceRes.Mesh)
Review Comment:
This condition checks that a discovery exists, but then adds
`instanceRes.Mesh` to `RegisterClusters` instead of using discovery attributes
(e.g., `d.Name`) or consistently using the discovery ID. As written, `d` is
only used as a nil-check, and the collected 'clusters' may be IDs rather than
user-facing names. Use a consistent value (either `d.Name` or the mesh ID) and
remove the redundant lookup if you intend to store IDs.
```suggestion
a.RegisterClusters.Add(d.Name)
```
##########
pkg/console/handler/configurator_rule.go:
##########
@@ -20,142 +20,134 @@ package handler
import (
"fmt"
"net/http"
- "strconv"
"strings"
+ "github.com/duke-git/lancet/v2/strutil"
"github.com/gin-gonic/gin"
- meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1"
+ "github.com/apache/dubbo-admin/pkg/common/bizerror"
+ "github.com/apache/dubbo-admin/pkg/common/constants"
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/store"
+ "github.com/apache/dubbo-admin/pkg/console/util"
+ meshresource
"github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1"
)
func ConfiguratorSearch(ctx consolectx.Context) gin.HandlerFunc {
return func(c *gin.Context) {
- req := model.NewSearchConfiguratorReq()
+ req := model.NewSearchReq()
if err := c.ShouldBindQuery(req); err != nil {
c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
return
}
- ruleList := &mesh.DynamicConfigResourceList{}
- var respList []model.ConfiguratorSearchResp
- if req.Keywords == "" {
- if err := ctx.ResourceManager().List(ctx.AppContext(),
ruleList, store.ListByPage(req.PageSize, strconv.Itoa(req.PageOffset))); err !=
nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
- return
- }
+ var searchResult *model.SearchPaginationResult
+ var err error
+ if strutil.IsBlank(req.Keywords) {
+ searchResult, err =
service.PageListConfiguratorRule(ctx, req)
} else {
- if err := ctx.ResourceManager().List(ctx.AppContext(),
ruleList, store.ListByNameContains(req.Keywords),
store.ListByPage(req.PageSize, strconv.Itoa(req.PageOffset))); err != nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
- return
- }
- }
- for _, item := range ruleList.Items {
- respList = append(respList,
model.ConfiguratorSearchResp{
- RuleName: item.Meta.GetName(),
- Scope: item.Spec.GetScope(),
- CreateTime:
item.Meta.GetCreationTime().String(),
- Enabled: item.Spec.GetEnabled(),
- })
- }
- result := model.NewSearchPaginationResult()
- result.List = respList
- result.PageInfo = &ruleList.Pagination
- c.JSON(http.StatusOK, model.NewSuccessResp(result))
+ searchResult, err =
service.SearchConfiguratorRuleByKeywords(ctx, req)
+ }
+ if err != nil {
+ util.HandleServiceError(c, err)
+ return
+ }
+ c.JSON(http.StatusOK, model.NewSuccessResp(searchResult))
}
}
func GetConfiguratorWithRuleName(ctx consolectx.Context) gin.HandlerFunc {
return func(c *gin.Context) {
- var name string
ruleName := c.Param("ruleName")
- if strings.HasSuffix(ruleName, consts.ConfiguratorRuleSuffix) {
- name =
ruleName[:len(ruleName)-len(consts.ConfiguratorRuleSuffix)]
- } else {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(fmt.Sprintf("ruleName must end with %s",
consts.ConfiguratorRuleSuffix)))
+ mesh := c.Query("mesh")
+ if strutil.IsBlank(ruleName) {
+ c.JSON(http.StatusBadRequest,
model.NewErrorResp("ruleName cannot be empty"))
+ return
+ }
+ if strutil.IsBlank(mesh) {
+ c.JSON(http.StatusBadRequest, model.NewErrorResp("mesh
cannot be empty"))
return
}
- res, err := service.GetConfigurator(ctx, name)
+ res, err := service.GetConfigurator(ctx, ruleName, mesh)
if err != nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
+ util.HandleServiceError(c, err)
return
}
+ if res == nil {
+ c.JSON(http.StatusOK, model.NewBizErrorResp(
+ bizerror.New(bizerror.NotFoundError,
fmt.Sprintf("%s not found", ruleName))))
Review Comment:
After returning a NotFound response when `res == nil`, the handler continues
and dereferences `res.Spec`, which will panic. Add a `return` after the
NotFound JSON response to stop execution.
```suggestion
bizerror.New(bizerror.NotFoundError,
fmt.Sprintf("%s not found", ruleName))))
return
```
##########
pkg/console/handler/tag_rule.go:
##########
@@ -20,95 +20,91 @@ package handler
import (
"fmt"
"net/http"
- "strconv"
"strings"
+ "github.com/duke-git/lancet/v2/strutil"
"github.com/gin-gonic/gin"
- meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1"
+ "github.com/apache/dubbo-admin/pkg/common/bizerror"
+ "github.com/apache/dubbo-admin/pkg/common/constants"
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/store"
+ "github.com/apache/dubbo-admin/pkg/console/util"
+ meshresource
"github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1"
)
func TagRuleSearch(ctx consolectx.Context) gin.HandlerFunc {
return func(c *gin.Context) {
+
req := model.NewSearchReq()
if err := c.ShouldBindQuery(req); err != nil {
c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
return
}
- resList := &mesh.TagRouteResourceList{}
- if req.Keywords == "" {
- if err := ctx.ResourceManager().List(ctx.AppContext(),
resList, store.ListByPage(req.PageSize, strconv.Itoa(req.PageOffset))); err !=
nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
- return
- }
+ var searchResult *model.SearchPaginationResult
+ var err error
+ if strutil.IsBlank(req.Keywords) {
+ searchResult, err = service.PageListTagRule(ctx, req)
} else {
- if err := ctx.ResourceManager().List(ctx.AppContext(),
resList, store.ListByNameContains(req.Keywords), store.ListByPage(req.PageSize,
strconv.Itoa(req.PageOffset))); err != nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
- return
- }
- }
- var respList []model.TagRuleSearchResp
- for _, item := range resList.Items {
- time := item.Meta.GetCreationTime().String()
- name := item.Meta.GetName()
- respList = append(respList, model.TagRuleSearchResp{
- CreateTime: &time,
- Enabled: &item.Spec.Enabled,
- RuleName: &name,
- })
- }
- result := model.NewSearchPaginationResult()
- result.List = respList
- result.PageInfo = &resList.Pagination
- c.JSON(http.StatusOK, model.NewSuccessResp(result))
+ searchResult, err =
service.SearchTagRuleByKeywords(ctx, req)
+ }
+ if err != nil {
+ util.HandleServiceError(c, err)
+ return
+ }
+ c.JSON(http.StatusOK, model.NewSuccessResp(searchResult))
}
}
func GetTagRuleWithRuleName(ctx consolectx.Context) gin.HandlerFunc {
return func(c *gin.Context) {
- var name string
ruleName := c.Param("ruleName")
- if strings.HasSuffix(ruleName, consts.TagRuleSuffix) {
- name =
ruleName[:len(ruleName)-len(consts.TagRuleSuffix)]
- } else {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(fmt.Sprintf("ruleName must end with %s",
consts.TagRuleSuffix)))
+ mesh := c.Query("mesh")
+ if !strings.HasSuffix(ruleName, constants.TagRuleDotSuffix) {
+ err := bizerror.New(bizerror.InvalidArgument,
fmt.Sprintf("ruleName must end with %s", constants.TagRuleDotSuffix))
+ c.JSON(http.StatusBadRequest,
model.NewBizErrorResp(err))
return
}
- if res, err := service.GetTagRule(ctx, name); err != nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
+ res, err := service.GetTagRule(ctx, ruleName, mesh)
+ if err != nil {
+ util.HandleServiceError(c, err)
return
- } else {
- c.JSON(http.StatusOK, model.GenTagRouteResp(res.Spec))
}
+ if res == nil {
+ util.HandleNotFoundError(c, ruleName)
+ return
+ }
+ c.JSON(http.StatusOK, model.GenTagRouteResp(res.Spec))
}
}
func PutTagRuleWithRuleName(ctx consolectx.Context) gin.HandlerFunc {
return func(c *gin.Context) {
- var name string
ruleName := c.Param("ruleName")
- if strings.HasSuffix(ruleName, consts.TagRuleSuffix) {
- name =
ruleName[:len(ruleName)-len(consts.TagRuleSuffix)]
- } else {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(fmt.Sprintf("ruleName must end with %s",
consts.TagRuleSuffix)))
+ mesh := c.Query("mesh")
+ if !strings.HasSuffix(ruleName, constants.TagRuleDotSuffix) {
+ err := bizerror.New(bizerror.InvalidArgument,
fmt.Sprintf("ruleName must end with %s", constants.TagRuleDotSuffix))
+ c.JSON(http.StatusBadRequest,
model.NewBizErrorResp(err))
return
}
- res := &mesh.TagRouteResource{
- Meta: nil,
- Spec: &meshproto.TagRoute{},
+ tagRuleRes, err := service.GetTagRule(ctx, ruleName, mesh)
+ if err != nil {
+ util.HandleServiceError(c, err)
+ return
}
- err := c.Bind(res.Spec)
+ if tagRuleRes == nil {
+ util.HandleNotFoundError(c, ruleName)
+ return
+ }
+ res := meshresource.NewTagRouteResourceWithAttributes(ruleName,
mesh)
+ err = c.Bind(res.Spec)
if err != nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
+ c.JSON(http.StatusOK, model.NewErrorResp(err.Error()))
return
}
- if err = service.UpdateTagRule(ctx, name, res); err != nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
+ if err = service.UpdateTagRule(ctx, res); err != nil {
+ c.JSON(http.StatusOK, model.NewErrorResp(err.Error()))
Review Comment:
Binding and update failures are returned with `http.StatusOK`, while other
handlers in this PR use `http.StatusBadRequest` (binding) and
`util.HandleServiceError` (service failures). Returning 200 for invalid input
makes client-side error handling harder and is inconsistent within the same API
surface. Consider returning `http.StatusBadRequest` for bind errors and using
the shared `util.HandleServiceError` for service-layer errors.
##########
pkg/console/model/observability.go:
##########
@@ -17,37 +17,26 @@
package model
-type DashboardReq interface {
- GetKeyVariable() string
-}
-
type AppDashboardReq struct {
- Application string `form:"application"`
-}
-
-func (req *AppDashboardReq) GetKeyVariable() string {
- return req.Application
+ AppName string `form:"appName"`
+ Mesh string `form:"appName"`
Review Comment:
`AppDashboardReq.Mesh` is tagged as `form:\"appName\"`, so `mesh` will never
bind from query parameters and will overwrite/duplicate appName binding
semantics. This should be tagged as `form:\"mesh\"` (and similarly
validated/used downstream) to avoid generating incorrect Grafana URLs and
mesh-scoped queries.
```suggestion
Mesh string `form:"mesh"`
```
##########
pkg/config/diagnostics/config.go:
##########
@@ -18,19 +18,23 @@
package diagnostics
import (
+ "github.com/apache/dubbo-admin/pkg/common/bizerror"
"github.com/apache/dubbo-admin/pkg/config"
)
type Config struct {
config.BaseConfig
// Port of Diagnostic Server for checking health and readiness of the
Control Plane
- ServerPort uint32 `json:"serverPort"
envconfig:"DUBBO_DIAGNOSTICS_SERVER_PORT"`
+ ServerPort uint32 `json:"serverPort"`
}
var _ = &Config{}
-func (d *Config) Validate() error {
+func (c *Config) Validate() error {
+ if c.ServerPort < 1 || c.ServerPort > 65535 {
+ return bizerror.New(bizerror.ConfigError, "server port for
diagnotics must between 1 to 65535")
Review Comment:
Typo in error message: 'diagnotics' should be 'diagnostics' (and the
phrasing is ungrammatical). Consider updating to a clearer message such as
'server port for diagnostics must be between 1 and 65535'.
```suggestion
return bizerror.New(bizerror.ConfigError, "server port for
diagnostics must be between 1 and 65535")
```
##########
pkg/console/handler/search.go:
##########
@@ -25,44 +25,42 @@ import (
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"
+ coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model"
)
func BannerGlobalSearch(ctx consolectx.Context) gin.HandlerFunc {
return func(c *gin.Context) {
- // 参考 API 定义 request 参数
req := model.NewSearchReq()
if err := c.ShouldBindQuery(req); err != nil {
c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
return
}
- // 根据 request 分流调用,如服务未实现继续实现
-
var res *model.SearchRes
switch req.SearchType {
case "ip":
- instances, _ := service.BannerSearchIp(ctx, req)
+ instances, _ := service.SearchInstanceByIp(ctx, req)
res = convertInstancesToSearchRes(instances)
case "instanceName":
- instances, _ := service.BannerSearchInstances(ctx, req)
+ instances, _ := service.SearchInstanceByName(ctx, req)
res = convertInstancesToSearchRes(instances)
case "appName":
- applications, _ :=
service.BannerSearchApplications(ctx, req)
+ applications, _ :=
service.SearchApplicationsByKeywords(ctx, req)
res = convertApplicationsToSearchRes(applications)
Review Comment:
Errors are ignored (`_ := ...`) and the conversion helpers assume non-nil
results; if the service returns an error (and/or nil), this can lead to panics
or misleading empty responses. Handle the returned error and validate
`instances/applications` are non-nil before converting; route failures through
the existing error response mechanism.
##########
pkg/console/handler/configurator_rule.go:
##########
@@ -20,142 +20,134 @@ package handler
import (
"fmt"
"net/http"
- "strconv"
"strings"
+ "github.com/duke-git/lancet/v2/strutil"
"github.com/gin-gonic/gin"
- meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1"
+ "github.com/apache/dubbo-admin/pkg/common/bizerror"
+ "github.com/apache/dubbo-admin/pkg/common/constants"
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/store"
+ "github.com/apache/dubbo-admin/pkg/console/util"
+ meshresource
"github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1"
)
func ConfiguratorSearch(ctx consolectx.Context) gin.HandlerFunc {
return func(c *gin.Context) {
- req := model.NewSearchConfiguratorReq()
+ req := model.NewSearchReq()
if err := c.ShouldBindQuery(req); err != nil {
c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
return
}
- ruleList := &mesh.DynamicConfigResourceList{}
- var respList []model.ConfiguratorSearchResp
- if req.Keywords == "" {
- if err := ctx.ResourceManager().List(ctx.AppContext(),
ruleList, store.ListByPage(req.PageSize, strconv.Itoa(req.PageOffset))); err !=
nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
- return
- }
+ var searchResult *model.SearchPaginationResult
+ var err error
+ if strutil.IsBlank(req.Keywords) {
+ searchResult, err =
service.PageListConfiguratorRule(ctx, req)
} else {
- if err := ctx.ResourceManager().List(ctx.AppContext(),
ruleList, store.ListByNameContains(req.Keywords),
store.ListByPage(req.PageSize, strconv.Itoa(req.PageOffset))); err != nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
- return
- }
- }
- for _, item := range ruleList.Items {
- respList = append(respList,
model.ConfiguratorSearchResp{
- RuleName: item.Meta.GetName(),
- Scope: item.Spec.GetScope(),
- CreateTime:
item.Meta.GetCreationTime().String(),
- Enabled: item.Spec.GetEnabled(),
- })
- }
- result := model.NewSearchPaginationResult()
- result.List = respList
- result.PageInfo = &ruleList.Pagination
- c.JSON(http.StatusOK, model.NewSuccessResp(result))
+ searchResult, err =
service.SearchConfiguratorRuleByKeywords(ctx, req)
+ }
+ if err != nil {
+ util.HandleServiceError(c, err)
+ return
+ }
+ c.JSON(http.StatusOK, model.NewSuccessResp(searchResult))
}
}
func GetConfiguratorWithRuleName(ctx consolectx.Context) gin.HandlerFunc {
return func(c *gin.Context) {
- var name string
ruleName := c.Param("ruleName")
- if strings.HasSuffix(ruleName, consts.ConfiguratorRuleSuffix) {
- name =
ruleName[:len(ruleName)-len(consts.ConfiguratorRuleSuffix)]
- } else {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(fmt.Sprintf("ruleName must end with %s",
consts.ConfiguratorRuleSuffix)))
+ mesh := c.Query("mesh")
+ if strutil.IsBlank(ruleName) {
+ c.JSON(http.StatusBadRequest,
model.NewErrorResp("ruleName cannot be empty"))
+ return
+ }
+ if strutil.IsBlank(mesh) {
+ c.JSON(http.StatusBadRequest, model.NewErrorResp("mesh
cannot be empty"))
return
}
- res, err := service.GetConfigurator(ctx, name)
+ res, err := service.GetConfigurator(ctx, ruleName, mesh)
if err != nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
+ util.HandleServiceError(c, err)
return
}
+ if res == nil {
+ c.JSON(http.StatusOK, model.NewBizErrorResp(
+ bizerror.New(bizerror.NotFoundError,
fmt.Sprintf("%s not found", ruleName))))
+ }
c.JSON(http.StatusOK, model.GenDynamicConfigToResp(res.Spec))
}
}
func PutConfiguratorWithRuleName(ctx consolectx.Context) gin.HandlerFunc {
return func(c *gin.Context) {
- var name string
ruleName := c.Param("ruleName")
- if strings.HasSuffix(ruleName, consts.ConfiguratorRuleSuffix) {
- name =
ruleName[:len(ruleName)-len(consts.ConfiguratorRuleSuffix)]
- } else {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(fmt.Sprintf("ruleName must end with %s",
consts.ConfiguratorRuleSuffix)))
+ mesh := c.Query("mesh")
+ if !strings.HasSuffix(ruleName,
constants.ConfiguratorRuleDotSuffix) {
+ c.JSON(http.StatusOK,
model.NewBizErrorResp(bizerror.New(bizerror.InvalidArgument,
+ fmt.Sprintf("dynamic config name must end with
%s", constants.ConfiguratorRuleDotSuffix))))
return
}
- res := &mesh.DynamicConfigResource{
- Meta: nil,
- Spec: &meshproto.DynamicConfig{},
- }
+ res :=
meshresource.NewDynamicConfigResourceWithAttributes(ruleName, mesh)
err := c.Bind(res.Spec)
if err != nil {
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
+ util.HandleArgumentError(c, err)
return
}
- if err = service.UpdateConfigurator(ctx, name, res); err != nil
{
- c.JSON(http.StatusBadRequest,
model.NewErrorResp(err.Error()))
+ configurator, err := service.GetConfigurator(ctx, ruleName,
mesh)
+ if err != nil {
+ util.HandleServiceError(c, err)
+ return
+ }
+ if configurator == nil {
+ c.JSON(http.StatusOK, model.NewBizErrorResp(
+ bizerror.New(bizerror.NotFoundError,
fmt.Sprintf("%s not found", ruleName))))
+ }
+ if err = service.UpdateConfigurator(ctx, res); err != nil {
+ c.JSON(http.StatusOK, model.NewErrorResp(err.Error()))
return
Review Comment:
Same control-flow issue as the GET handler: when `configurator == nil`, the
handler writes a NotFound response but then continues to update anyway. Add
`return` after the NotFound response, otherwise the API can incorrectly report
success/error after already responding.
##########
pkg/console/service/configurator_rule.go:
##########
@@ -18,50 +18,154 @@
package service
import (
- meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1"
+ "github.com/apache/dubbo-admin/pkg/common/constants"
+ "github.com/apache/dubbo-admin/pkg/core/lock"
+ "github.com/duke-git/lancet/v2/slice"
+
+ "github.com/apache/dubbo-admin/pkg/common/bizerror"
consolectx "github.com/apache/dubbo-admin/pkg/console/context"
- "github.com/apache/dubbo-admin/pkg/core/consts"
+ "github.com/apache/dubbo-admin/pkg/console/model"
"github.com/apache/dubbo-admin/pkg/core/logger"
- coreresource
"github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1"
+ "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"
- "github.com/apache/dubbo-admin/pkg/core/store"
+ "github.com/apache/dubbo-admin/pkg/core/store/index"
)
-func GetConfigurator(ctx consolectx.Context, name string)
(*meshproto.DynamicConfig, error) {
- res := &coreresource.DynamicConfig{Spec: &meshproto.DynamicConfig{}}
- if err := ctx.ResourceManager().Get(ctx.AppContext(), res,
- // here `name` may be service-name or app-name, set
*ByApplication(`name`) is ok.
- store.GetByApplication(name),
store.GetByKey(name+consts.ConfiguratorRuleSuffix, coremodel.DefaultMesh)); err
!= nil {
- logger.Warnf("get %s configurator failed with error: %s", name,
err.Error())
+func PageListConfiguratorRule(ctx consolectx.Context, req *model.SearchReq)
(*model.SearchPaginationResult, error) {
+ pageData, err :=
manager.PageListByIndexes[*meshresource.DynamicConfigResource](
+ ctx.ResourceManager(),
+ meshresource.DynamicConfigKind,
+ map[string]string{
+ index.ByMeshIndex: req.Mesh,
+ },
+ req.PageReq)
+ if err != nil {
+ logger.Errorf("search dynamic config rule error: %v", err)
+ return nil, bizerror.New(bizerror.InternalError, "search
dynamic config rule failed, please try again")
+ }
+ if pageData.Data == nil || len(pageData.Data) == 0 {
+ return &model.SearchPaginationResult{
+ List: nil,
+ PageInfo: coremodel.Pagination{
+ Total: 0,
+ PageSize: req.PageReq.PageSize,
+ PageOffset: req.PageReq.PageOffset,
+ },
+ }, nil
+ }
+ respList := slice.Map(pageData.Data, func(_ int, item
*meshresource.DynamicConfigResource) *model.ConfiguratorSearchResp {
+ return &model.ConfiguratorSearchResp{
+ Scope: item.Spec.Scope,
+ CreateTime: "",
+ Enabled: item.Spec.Enabled,
+ RuleName: item.Name,
+ }
Review Comment:
`CreateTime` is always returned as an empty string for configurator rules,
unlike condition rules where creation timestamp is populated. If the underlying
resource provides timestamps (e.g., `item.CreationTimestamp`), populate it here
to avoid regressions in list UX/API consumers expecting meaningful `createTime`.
##########
pkg/config/loader.go:
##########
@@ -18,39 +18,38 @@
package config
import (
+ "fmt"
"os"
- "github.com/kelseyhightower/envconfig"
"github.com/pkg/errors"
"sigs.k8s.io/yaml"
- "github.com/apache/dubbo-admin/pkg/core"
+ "github.com/apache/dubbo-admin/pkg/common/bizerror"
)
func Load(file string, cfg Config) error {
- return LoadWithOption(file, cfg, false, true, true)
+ return LoadWithOption(file, cfg, false, true)
}
-func LoadWithOption(file string, cfg Config, strict bool, includeEnv bool,
validate bool) error {
+func LoadWithOption(file string, cfg Config, strict bool, validate bool) error
{
if file == "" {
- core.Log.WithName("config").Info("skipping reading config from
file")
- } else if err := loadFromFile(file, cfg, strict); err != nil {
- return err
+ return bizerror.New(bizerror.ConfigError, "config file is
needed")
+ }
+ if err := loadFromFile(file, cfg, strict); err != nil {
+ return fmt.Errorf("configuration loading failed, %w", err)
}
Review Comment:
This changes config loading behavior to require a config file and removes
environment-variable overlay support (previously supported via
`envconfig.Process`). That’s a breaking behavior change for deployments relying
on env-only configuration. If backward compatibility is required, consider
keeping env overlay optional (or at least preserving the previous
`LoadWithOption(... includeEnv ...)` path) and documenting the new requirement.
##########
pkg/console/handler/prometheus.go:
##########
@@ -17,30 +17,44 @@
package handler
-// proxy for prometheus
-
import (
+ "io"
"net/http"
- "net/http/httputil"
- "net/url"
"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"
)
func PromQL(ctx consolectx.Context) gin.HandlerFunc {
return func(c *gin.Context) {
- query := c.Request.URL.Query().Get("query")
- values := url.Values{}
- values.Add("query", query)
- promUrl := ctx.Config().Console.Prometheus + "/api/v1/query?" +
values.Encode()
- proxyUrl, _ := url.Parse(promUrl)
- director := func(req *http.Request) {
- req.URL.Scheme = proxyUrl.Scheme
- req.URL.Host = proxyUrl.Host
- req.Host = proxyUrl.Host
- req.URL.Path = proxyUrl.Path
+ promBaseUrl := ctx.Config().Observability.PrometheusBaseURL
+ if promBaseUrl == nil {
+ c.JSON(http.StatusOK, model.NewBizErrorResp(
+ bizerror.New(bizerror.ConfigError, "Please
configure prometheus url to retrieve metrics")))
+ return
+ }
+
+ u := *promBaseUrl
+ u.RawQuery = c.Request.URL.RawQuery
+ u.Path = "/api/v1/query"
+ s := u.String()
+ resp, err := http.Get(s)
+ if err != nil {
+ c.JSON(http.StatusOK, model.NewBizErrorResp(
+ bizerror.New(bizerror.NetWorkError,
err.Error())))
+ return
+ }
+ defer resp.Body.Close()
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ c.JSON(http.StatusOK, model.NewBizErrorResp(
+ bizerror.New(bizerror.NetWorkError,
err.Error())))
+ return
}
- proxy := &httputil.ReverseProxy{Director: director}
- proxy.ServeHTTP(c.Writer, c.Request)
+ c.Data(http.StatusOK, resp.Header.Get("Content-Type"), body)
Review Comment:
The handler uses `http.Get` without any timeout and ignores Prometheus
response status codes (always returns `http.StatusOK` to the client). This can
hang requests under network issues and can mask upstream 4xx/5xx as successful
responses. Use an `http.Client` with a reasonable timeout (and preferably
`http.NewRequestWithContext` using `c.Request.Context()`), and propagate
`resp.StatusCode` when returning `c.Data(...)` (or map it appropriately).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]