This is an automated email from the ASF dual-hosted git repository.

min pushed a commit to branch refactor-with-go
in repository https://gitbox.apache.org/repos/asf/dubbo-admin.git


The following commit(s) were added to refs/heads/refactor-with-go by this push:
     new de6f94b1 Cluster overview backend implementation (#1149)
de6f94b1 is described below

commit de6f94b146372261d453255a71d16606510891a6
Author: Ken Liu <ken.lj...@gmail.com>
AuthorDate: Tue May 30 21:17:04 2023 +0800

    Cluster overview backend implementation (#1149)
    
    * update link
    
    * Cluster overview support
    
    * fix ut
    
    * fix lint
    
    * fix unit tests
    
    ---------
    
    Co-authored-by: ken.lj <ken...@alibaba-inc.com>
---
 pkg/admin/handlers/service.go                      |  55 +++++-
 pkg/admin/model/monitor.go                         |  22 ++-
 pkg/admin/router/router.go                         |   7 +-
 pkg/admin/services/monitor_service.go              |   7 +-
 pkg/admin/services/prometheus_service_impl.go      | 184 +++++++++++++--------
 pkg/admin/services/provider_service.go             |  11 +-
 pkg/admin/services/provider_service_impl.go        | 170 ++++++++++++++++---
 pkg/admin/services/provider_service_impl_test.go   |  14 +-
 pkg/admin/services/route_service_impl.go           |  23 +++
 pkg/admin/services/traffic/accesslog.go            |   2 +-
 pkg/admin/services/traffic/argument.go             |   2 +-
 .../services/traffic/generic_rule_operation.go     |  22 ---
 pkg/admin/services/traffic/gray.go                 |   2 +-
 pkg/admin/services/traffic/mock.go                 |   2 +-
 pkg/admin/services/traffic/region.go               |   2 +-
 pkg/admin/services/traffic/retry.go                |   2 +-
 pkg/admin/services/traffic/timeout.go              |   2 +-
 pkg/admin/services/traffic/weight.go               |   2 +-
 18 files changed, 383 insertions(+), 148 deletions(-)

diff --git a/pkg/admin/handlers/service.go b/pkg/admin/handlers/service.go
index 47b728c5..e9124a5b 100644
--- a/pkg/admin/handlers/service.go
+++ b/pkg/admin/handlers/service.go
@@ -41,9 +41,9 @@ import (
 )
 
 var (
-       providerService   services.ProviderService = 
&services.ProviderServiceImpl{}
-       consumerService   services.ConsumerService = 
&services.ConsumerServiceImpl{}
-       prometheusService services.MonitorService  = 
&services.PrometheusServiceImpl{}
+       providerService services.ProviderService = 
&services.ProviderServiceImpl{}
+       consumerService services.ConsumerService = 
&services.ConsumerServiceImpl{}
+       monitorService  services.MonitorService  = 
&services.PrometheusServiceImpl{}
 )
 
 // AllServices get all services
@@ -231,12 +231,46 @@ func ServiceDetail(c *gin.Context) {
        c.JSON(http.StatusOK, serviceDetail)
 }
 
+// Version show basic information of the Admin process
+// @Summary      show basic information of the Admin process
+// @Description  show basic information of the Admin process
+// @Tags         metrics
+// @Accept       json
+// @Produce      json
+// @Success      200  {object}  version.Version
+// @Router       /api/{env}/version [get]
 func Version(c *gin.Context) {
        c.JSON(http.StatusOK, version.GetVersion())
 }
 
+// FlowMetrics show Prometheus collected metrics
+// @Summary      show Prometheus collected metrics
+// @Description  show Prometheus collected metrics
+// @Tags         metrics
+// @Accept       json
+// @Produce      json
+// @Success      200  {object}  model.FlowMetricsRes
+// @Failure      500  {object}  model.HTTPError
+// @Router       /api/{env}/metrics/flow [get]
 func FlowMetrics(c *gin.Context) {
-       res, err := prometheusService.FlowMetrics()
+       res, err := monitorService.FlowMetrics()
+       if err != nil {
+               c.JSON(http.StatusInternalServerError, model.HTTPError{Error: 
err.Error()})
+       }
+       c.JSON(http.StatusOK, res)
+}
+
+// ClusterMetrics show cluster overview
+// @Summary      show cluster overview
+// @Description  show cluster overview
+// @Tags         metrics
+// @Accept       json
+// @Produce      json
+// @Success      200  {object}  model.ClusterMetricsRes
+// @Failure      500  {object}  model.HTTPError
+// @Router       /api/{env}/metrics/cluster [get]
+func ClusterMetrics(c *gin.Context) {
+       res, err := monitorService.ClusterMetrics()
        if err != nil {
                c.JSON(http.StatusInternalServerError, gin.H{
                        "error": err.Error(),
@@ -245,8 +279,17 @@ func FlowMetrics(c *gin.Context) {
        c.JSON(http.StatusOK, res)
 }
 
-func ClusterMetrics(c *gin.Context) {
-       res, err := prometheusService.ClusterMetrics()
+// Metadata show metadata of the cluster, like dubbo versions, protocols, etc.
+// @Summary      show metadata of the cluster, like dubbo versions, protocols, 
etc.
+// @Description  show metadata of the cluster, like dubbo versions, protocols, 
etc.
+// @Tags         metrics
+// @Accept       json
+// @Produce      json
+// @Success      200  {object}  model.Metadata
+// @Failure      500  {object}  model.HTTPError
+// @Router       /api/{env}/metrics/metadata [get]
+func Metadata(c *gin.Context) {
+       res, err := monitorService.ClusterMetrics()
        if err != nil {
                c.JSON(http.StatusInternalServerError, gin.H{
                        "error": err.Error(),
diff --git a/pkg/admin/model/monitor.go b/pkg/admin/model/monitor.go
index d4767841..759e90a3 100644
--- a/pkg/admin/model/monitor.go
+++ b/pkg/admin/model/monitor.go
@@ -15,12 +15,24 @@
 
 package model
 
-type Response struct {
-       Status int    `json:"status"`
-       Data   string `json:"data"`
-}
-
 type Target struct {
        Targets []string          `json:"targets"`
        Labels  map[string]string `json:"labels"`
 }
+
+type ClusterMetricsRes struct {
+       Data map[string]int `json:"data"`
+}
+
+type FlowMetricsRes struct {
+       Data map[string]float64 `json:"data"`
+}
+
+type Metadata struct {
+       Versions       []interface{} `json:"versions"`
+       ConfigCenter   string        `json:"configCenter"`
+       Registry       string        `json:"registry"`
+       MetadataCenter string        `json:"metadataCenter"`
+       Protocols      []interface{} `json:"protocols"`
+       Rules          []string      `json:"rules"`
+}
diff --git a/pkg/admin/router/router.go b/pkg/admin/router/router.go
index 4eff69bb..6ed20ccd 100644
--- a/pkg/admin/router/router.go
+++ b/pkg/admin/router/router.go
@@ -38,9 +38,10 @@ func InitRouter() *gin.Engine {
                server.GET("/service/:service", handlers.ServiceDetail)
        }
 
-       router.GET("/api/dev/version", handlers.Version)
-       router.GET("/api/dev/metrics/flow", handlers.FlowMetrics)
-       router.GET("/api/dev/metrics/cluster", handlers.ClusterMetrics)
+       router.GET("/api/:env/version", handlers.Version)
+       router.GET("/api/:env/metrics/flow", handlers.FlowMetrics)
+       router.GET("/api/:env/metrics/cluster", handlers.ClusterMetrics)
+       router.GET("/api/:env/metrics/metadata", handlers.Metadata)
 
        override := router.Group("/api/:env/rules/override")
        {
diff --git a/pkg/admin/services/monitor_service.go 
b/pkg/admin/services/monitor_service.go
index fdec3698..316d9d18 100644
--- a/pkg/admin/services/monitor_service.go
+++ b/pkg/admin/services/monitor_service.go
@@ -22,7 +22,8 @@ import (
 )
 
 type MonitorService interface {
-       FlowMetrics() ([]model.Response, error)    // Traffic overview
-       ClusterMetrics() ([]model.Response, error) // Cluster overview
-       PromDiscovery(w http.ResponseWriter) error // prometheus http_sd 
discovery
+       FlowMetrics() (model.FlowMetricsRes, error)       // Traffic overview
+       ClusterMetrics() (model.ClusterMetricsRes, error) // Cluster overview
+       PromDiscovery(w http.ResponseWriter) error        // prometheus http_sd 
discovery
+       Metadata() (model.Metadata, error)
 }
diff --git a/pkg/admin/services/prometheus_service_impl.go 
b/pkg/admin/services/prometheus_service_impl.go
index 3b5b546b..87aed7a2 100644
--- a/pkg/admin/services/prometheus_service_impl.go
+++ b/pkg/admin/services/prometheus_service_impl.go
@@ -20,16 +20,16 @@ import (
        "encoding/json"
        "fmt"
        "net/http"
-       "strconv"
        "time"
 
+       set "github.com/dubbogo/gost/container/set"
+
        "github.com/prometheus/client_golang/api"
        prom_v1 "github.com/prometheus/client_golang/api/prometheus/v1"
 
        "github.com/apache/dubbo-admin/pkg/admin/config"
        "github.com/apache/dubbo-admin/pkg/admin/constant"
        "github.com/apache/dubbo-admin/pkg/admin/model"
-       "github.com/apache/dubbo-admin/pkg/admin/model/util"
        util2 "github.com/apache/dubbo-admin/pkg/admin/util"
        "github.com/apache/dubbo-admin/pkg/logger"
        "github.com/apache/dubbo-admin/pkg/monitor/prometheus"
@@ -46,25 +46,25 @@ type PrometheusServiceImpl struct{}
 func (p *PrometheusServiceImpl) PromDiscovery(w http.ResponseWriter) error {
        w.Header().Set("Content-Type", "application/json")
        // Reduce the call chain and improve performance.
+
+       // Find all provider addresses
        proAddr, err := providerServiceImpl.findAddresses()
        if err != nil {
                logger.Sugar().Errorf("Error provider findAddresses: %v\n", err)
                return err
        }
-       var targets []string
-       for i := 0; i < len(proAddr); i++ {
-               targets = append(targets, util2.GetDiscoveryPath(proAddr[i]))
-       }
-       filterCon := make(map[string]string)
-       filterCon[constant.CategoryKey] = constant.ConsumersCategory
-       servicesMap, err := util.FilterFromCategory(filterCon)
-       if err != nil {
-               logger.Sugar().Errorf("Error filter category: %v\n", err)
-               return err
+       addresses := set.NewSet()
+       items := proAddr.Values()
+       for i := 0; i < len(items); i++ {
+               addresses.Add(util2.GetDiscoveryPath(items[i].(string)))
        }
-       for _, url := range servicesMap {
-               targets = append(targets, util2.GetDiscoveryPath(url.Location))
+
+       targets := make([]string, 0, addresses.Size())
+       items = addresses.Values()
+       for _, v := range items {
+               targets = append(targets, v.(string))
        }
+
        target := []model.Target{
                {
                        Targets: targets,
@@ -75,60 +75,57 @@ func (p *PrometheusServiceImpl) PromDiscovery(w 
http.ResponseWriter) error {
        return err
 }
 
-func (p *PrometheusServiceImpl) ClusterMetrics() ([]model.Response, error) {
-       res := make([]model.Response, 5)
+func (p *PrometheusServiceImpl) ClusterMetrics() (model.ClusterMetricsRes, 
error) {
+       res := model.ClusterMetricsRes{
+               Data: make(map[string]int),
+       }
+       // total application number
        applications, err := providerService.FindApplications()
        appNum := 0
        if err != nil {
                logger.Sugar().Errorf("Error find applications: %v\n", err)
-               res[0].Status = http.StatusInternalServerError
-               res[0].Data = ""
        } else {
-               appNum = len(applications)
-               res[0].Status = http.StatusOK
-               res[0].Data = strconv.Itoa(appNum)
+               appNum = applications.Size()
        }
+       res.Data["application"] = appNum
+
+       // total service number
        services, err := providerService.FindServices()
        svc := 0
        if err != nil {
                logger.Sugar().Errorf("Error find services: %v\n", err)
-               res[1].Status = http.StatusInternalServerError
-               res[1].Data = ""
        } else {
-               svc = len(services)
-               res[1].Status = http.StatusOK
-               res[1].Data = strconv.Itoa(svc)
+               svc = services.Size()
        }
+       res.Data["services"] = svc
+
        providers, err := providerService.FindService(constant.IP, 
constant.AnyValue)
        pro := 0
        if err != nil {
                logger.Sugar().Errorf("Error find providers: %v\n", err)
-               res[2].Status = http.StatusInternalServerError
-               res[2].Data = ""
        } else {
                pro = len(providers)
-               res[2].Status = http.StatusOK
-               res[2].Data = strconv.Itoa(pro)
        }
+       res.Data["providers"] = pro
+
        consumers, err := consumerService.FindAll()
        con := 0
        if err != nil {
                logger.Sugar().Errorf("Error find consumers: %v\n", err)
-               res[3].Status = http.StatusInternalServerError
-               res[3].Data = ""
        } else {
                con = len(consumers)
-               res[3].Status = http.StatusOK
-               res[3].Data = strconv.Itoa(con)
        }
-       allInstance := pro + con
-       res[5].Status = http.StatusOK
-       res[5].Data = strconv.Itoa(allInstance)
+       res.Data["consumers"] = con
+
+       res.Data["all"] = con
        return res, nil
 }
 
-func (p *PrometheusServiceImpl) FlowMetrics() ([]model.Response, error) {
-       res := make([]model.Response, 5)
+func (p *PrometheusServiceImpl) FlowMetrics() (model.FlowMetricsRes, error) {
+       res := model.FlowMetricsRes{
+               Data: make(map[string]float64),
+       }
+
        ip := config.PrometheusIp
        port := config.PrometheusPort
        address := fmt.Sprintf("http://%s:%s";, ip, port)
@@ -137,64 +134,107 @@ func (p *PrometheusServiceImpl) FlowMetrics() 
([]model.Response, error) {
        })
        if err != nil {
                logger.Sugar().Errorf("Error creating client: %v\n", err)
-               return nil, err
+               return res, err
        }
        v1api := prom_v1.NewAPI(client)
        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
        defer cancel()
 
+       // qps
        vector1 := prometheus.FetchQuery(ctx, v1api, constant.MetricsQps, nil)
        err = vector1.Err
+       qps := float64(0)
        if err != nil {
                logger.Sugar().Errorf("Error query qps: %v\n", err)
-               res[0].Status = http.StatusInternalServerError
-               res[0].Data = ""
        } else {
-               qps := float64(vector1.Vector[0].Value)
-               res[0].Status = http.StatusOK
-               res[0].Data = fmt.Sprintf("%d", int(qps))
+               qps = float64(vector1.Vector[0].Value)
+               res.Data["qps"] = qps
        }
-       vector2 := prometheus.FetchQuery(ctx, v1api, 
constant.MetricsHttpRequestSuccessCount, nil)
-       data1 := float64(vector2.Vector[0].Value)
+
+       // total count
        vector3 := prometheus.FetchQuery(ctx, v1api, 
constant.MetricsHttpRequestTotalCount, nil)
-       data2 := float64(vector3.Vector[0].Value)
-       if vector2.Err != nil && vector3.Err != nil {
-               res[1].Status = http.StatusInternalServerError
-               res[1].Data = ""
+       total := float64(0)
+       if vector3.Err != nil {
+               logger.Sugar().Errorf("Error query total count: %v\n", err)
        } else {
-               res[1].Status = http.StatusOK
-               successRate := data1 / data2
-               res[1].Data = fmt.Sprintf("%0.2f", successRate)
+               total = float64(vector3.Vector[0].Value)
+               res.Data["total"] = total
        }
+
+       // success count
+       vector2 := prometheus.FetchQuery(ctx, v1api, 
constant.MetricsHttpRequestSuccessCount, nil)
+       success := float64(0)
+       if vector2.Err != nil {
+               logger.Sugar().Errorf("Error query success count: %v\n", err)
+       } else {
+               success = float64(vector2.Vector[0].Value)
+               res.Data["total"] = success
+       }
+
+       // timeout count
        vector4 := prometheus.FetchQuery(ctx, v1api, 
constant.MetricsHttpRequestOutOfTimeCount, nil)
-       data4 := float64(vector4.Vector[0].Value)
+       timeout := float64(0)
        if vector4.Err != nil {
-               res[2].Status = http.StatusInternalServerError
-               res[2].Data = ""
+               logger.Sugar().Errorf("Error query timeout count: %v\n", err)
        } else {
-               res[2].Status = http.StatusOK
-               outOfTimeRate := data4 / data2
-               res[2].Data = fmt.Sprintf("%0.2f", outOfTimeRate)
+               timeout = float64(vector4.Vector[0].Value)
+               res.Data["timeout"] = timeout
        }
+
+       // address not found count
        vector5 := prometheus.FetchQuery(ctx, v1api, 
constant.MetricsHttpRequestAddressNotFount, nil)
-       data5 := float64(vector5.Vector[0].Value)
+       addrNotFound := float64(0)
        if vector5.Err != nil {
-               res[3].Status = http.StatusInternalServerError
-               res[3].Data = ""
+               logger.Sugar().Errorf("Error query address not found count: 
%v\n", err)
        } else {
-               res[3].Status = http.StatusOK
-               notFound := data5 / data2
-               res[3].Data = fmt.Sprintf("%0.2f", notFound)
+               addrNotFound = float64(vector5.Vector[0].Value)
+               res.Data["addressNotFound"] = addrNotFound
        }
+
+       // other exceptions count
        vector6 := prometheus.FetchQuery(ctx, v1api, 
constant.MetricsHttpRequestOtherException, nil)
-       data6 := float64(vector6.Vector[0].Value)
+       others := float64(0)
        if vector6.Err != nil {
-               res[4].Status = http.StatusInternalServerError
-               res[4].Data = ""
+               logger.Sugar().Errorf("Error query othere exceptions count: 
%v\n", err)
        } else {
-               res[4].Status = http.StatusOK
-               other := data6 / data2
-               res[4].Data = fmt.Sprintf("%0.2f", other)
+               others = float64(vector6.Vector[0].Value)
+               res.Data["others"] = others
        }
        return res, nil
 }
+
+func (p *PrometheusServiceImpl) Metadata() (model.Metadata, error) {
+       metadata := model.Metadata{}
+
+       // versions
+       versions, err := providerService.FindVersions()
+       if err != nil {
+               logger.Error("Failed to parse versions!")
+       }
+       metadata.Versions = versions.Values()
+
+       // protocols
+       protocols, err := providerService.FindProtocols()
+       if err != nil {
+               logger.Error("Failed to parse protocols!")
+       }
+       metadata.Protocols = protocols.Values()
+
+       // centers
+       metadata.Registry = config.RegistryCenter.GetURL().Location
+       metadata.MetadataCenter = config.RegistryCenter.GetURL().Location
+       metadata.ConfigCenter = config.RegistryCenter.GetURL().Location
+
+       // rules
+       rules, err := GetRules("")
+       if err != nil {
+               return model.Metadata{}, err
+       }
+       keys := make([]string, 0, len(rules))
+       for k := range rules {
+               keys = append(keys, k)
+       }
+       metadata.Rules = keys
+
+       return metadata, nil
+}
diff --git a/pkg/admin/services/provider_service.go 
b/pkg/admin/services/provider_service.go
index fc8ee2e3..be811275 100644
--- a/pkg/admin/services/provider_service.go
+++ b/pkg/admin/services/provider_service.go
@@ -15,11 +15,16 @@
 
 package services
 
-import "github.com/apache/dubbo-admin/pkg/admin/model"
+import (
+       "github.com/apache/dubbo-admin/pkg/admin/model"
+       set "github.com/dubbogo/gost/container/set"
+)
 
 type ProviderService interface {
-       FindServices() ([]string, error)
-       FindApplications() ([]string, error)
+       FindServices() (*set.HashSet, error)
+       FindApplications() (*set.HashSet, error)
+       FindProtocols() (*set.HashSet, error)
+       FindVersions() (*set.HashSet, error)
        FindService(string, string) ([]*model.ServiceDTO, error)
        FindByService(string) ([]*model.Provider, error)
 }
diff --git a/pkg/admin/services/provider_service_impl.go 
b/pkg/admin/services/provider_service_impl.go
index ff5df233..5e1d99a2 100644
--- a/pkg/admin/services/provider_service_impl.go
+++ b/pkg/admin/services/provider_service_impl.go
@@ -21,6 +21,8 @@ import (
        "strings"
        "sync"
 
+       set "github.com/dubbogo/gost/container/set"
+
        "dubbo.apache.org/dubbo-go/v3/common"
        "github.com/apache/dubbo-admin/pkg/admin/cache"
        "github.com/apache/dubbo-admin/pkg/admin/constant"
@@ -31,39 +33,57 @@ import (
 type ProviderServiceImpl struct{}
 
 // FindServices finds all services
-func (p *ProviderServiceImpl) FindServices() ([]string, error) {
-       var services []string
+func (p *ProviderServiceImpl) FindServices() (*set.HashSet, error) {
+       services := set.NewSet()
        servicesAny, ok := 
cache.InterfaceRegistryCache.Load(constant.ProvidersCategory)
        if !ok {
-               return nil, nil
+               return services, nil
        }
        servicesMap, ok := servicesAny.(*sync.Map)
        if !ok {
-               return nil, fmt.Errorf("servicesMap type not *sync.Map")
+               return services, fmt.Errorf("servicesMap type not *sync.Map")
        }
 
        servicesMap.Range(func(key, value any) bool {
-               services = append(services, key.(string))
+               services.Add(key.(string))
                return true
        })
        return services, nil
 }
 
 // FindApplications finds all applications
-func (p *ProviderServiceImpl) FindApplications() ([]string, error) {
+func (p *ProviderServiceImpl) FindApplications() (*set.HashSet, error) {
        var (
-               applications []string
+               applications = set.NewSet()
                err          error
        )
-       servicesAny, ok := 
cache.InterfaceRegistryCache.Load(constant.ProvidersCategory)
+       providersAny, ok := 
cache.InterfaceRegistryCache.Load(constant.ProvidersCategory)
        if !ok {
-               return nil, nil
+               return applications, nil
+       }
+       err = extractApplications(providersAny, applications)
+       if err != nil {
+               return applications, err
        }
+
+       consumersAny, ok := 
cache.InterfaceRegistryCache.Load(constant.ConsumersCategory)
+       if !ok {
+               return applications, nil
+       }
+       err = extractApplications(consumersAny, applications)
+       if err != nil {
+               return applications, err
+       }
+       return applications, err
+}
+
+func extractApplications(servicesAny any, applications *set.HashSet) error {
        servicesMap, ok := servicesAny.(*sync.Map)
        if !ok {
-               return nil, fmt.Errorf("servicesMap type not *sync.Map")
+               return fmt.Errorf("servicesMap type not *sync.Map")
        }
 
+       var err error
        servicesMap.Range(func(key, value any) bool {
                service, ok := value.(map[string]*common.URL)
                if !ok {
@@ -73,29 +93,48 @@ func (p *ProviderServiceImpl) FindApplications() ([]string, 
error) {
                for _, url := range service {
                        app := url.GetParam(constant.ApplicationKey, "")
                        if app != "" {
-                               applications = append(applications, app)
+                               applications.Add(app)
                        }
                }
                return true
        })
-       return applications, err
+       return err
 }
 
 // findAddresses finds all addresses
-func (p *ProviderServiceImpl) findAddresses() ([]string, error) {
+func (p *ProviderServiceImpl) findAddresses() (*set.HashSet, error) {
        var (
-               addresses []string
+               addresses = set.NewSet()
                err       error
        )
        servicesAny, ok := 
cache.InterfaceRegistryCache.Load(constant.ProvidersCategory)
        if !ok {
-               return nil, nil
+               return addresses, nil
+       }
+       err = extractAddresses(servicesAny, addresses)
+       if err != nil {
+               return addresses, err
+       }
+
+       consumersAny, ok := 
cache.InterfaceRegistryCache.Load(constant.ConsumersCategory)
+       if !ok {
+               return addresses, nil
        }
+       err = extractAddresses(consumersAny, addresses)
+       if err != nil {
+               return addresses, err
+       }
+
+       return addresses, err
+}
+
+func extractAddresses(servicesAny any, addresses *set.HashSet) error {
        servicesMap, ok := servicesAny.(*sync.Map)
        if !ok {
-               return nil, fmt.Errorf("servicesMap type not *sync.Map")
+               return fmt.Errorf("servicesMap type not *sync.Map")
        }
 
+       var err error
        servicesMap.Range(func(key, value any) bool {
                service, ok := value.(map[string]*common.URL)
                if !ok {
@@ -105,12 +144,101 @@ func (p *ProviderServiceImpl) findAddresses() ([]string, 
error) {
                for _, url := range service {
                        loc := url.Location
                        if loc != "" {
-                               addresses = append(addresses, loc)
+                               addresses.Add(loc)
                        }
                }
                return true
        })
-       return addresses, err
+       return err
+}
+
+// FindVersions finds all versions
+func (p *ProviderServiceImpl) FindVersions() (*set.HashSet, error) {
+       var (
+               versions = set.NewSet()
+               err      error
+       )
+       servicesAny, ok := 
cache.InterfaceRegistryCache.Load(constant.ProvidersCategory)
+       if !ok {
+               return versions, nil
+       }
+
+       err = extractVersions(servicesAny, versions)
+       if err != nil {
+               return versions, err
+       }
+
+       return versions, err
+}
+
+func extractVersions(servicesAny any, versions *set.HashSet) error {
+       servicesMap, ok := servicesAny.(*sync.Map)
+       if !ok {
+               return fmt.Errorf("servicesMap type not *sync.Map")
+       }
+
+       var err error
+       servicesMap.Range(func(key, value any) bool {
+               service, ok := value.(map[string]*common.URL)
+               if !ok {
+                       err = fmt.Errorf("service type not 
map[string]*common.URL")
+                       return false
+               }
+               for _, url := range service {
+                       release := url.GetParam("release", "")
+                       if release == "" {
+                               release = url.GetParam("revision", "")
+                       }
+                       if release != "" {
+                               versions.Add(release)
+                       }
+               }
+               return true
+       })
+       return err
+}
+
+// FindProtocols finds all protocols
+func (p *ProviderServiceImpl) FindProtocols() (*set.HashSet, error) {
+       var (
+               protocols = set.NewSet()
+               err       error
+       )
+       servicesAny, ok := 
cache.InterfaceRegistryCache.Load(constant.ProvidersCategory)
+       if !ok {
+               return protocols, nil
+       }
+
+       err = extractProtocols(servicesAny, protocols)
+       if err != nil {
+               return protocols, err
+       }
+
+       return protocols, err
+}
+
+func extractProtocols(servicesAny any, protocols *set.HashSet) error {
+       servicesMap, ok := servicesAny.(*sync.Map)
+       if !ok {
+               return fmt.Errorf("servicesMap type not *sync.Map")
+       }
+
+       var err error
+       servicesMap.Range(func(key, value any) bool {
+               service, ok := value.(map[string]*common.URL)
+               if !ok {
+                       err = fmt.Errorf("service type not 
map[string]*common.URL")
+                       return false
+               }
+               for _, url := range service {
+                       proto := url.Protocol
+                       if proto != "" && proto != "consumer" {
+                               protocols.Add(proto)
+                       }
+               }
+               return true
+       })
+       return err
 }
 
 // FindByService finds providers by service name and returns a list of 
providers
@@ -176,7 +304,7 @@ func (p *ProviderServiceImpl) FindService(pattern string, 
filter string) ([]*mod
                        return nil, fmt.Errorf("unsupport the pattern: %s", 
pattern)
                }
        } else {
-               var candidates []string
+               var candidates *set.HashSet
                if pattern == constant.IP {
                        candidates, err = p.findAddresses()
                        if err != nil {
@@ -204,7 +332,9 @@ func (p *ProviderServiceImpl) FindService(pattern string, 
filter string) ([]*mod
                if err != nil {
                        return nil, err
                }
-               for _, candidate := range candidates {
+               items := candidates.Values()
+               for _, candidateAny := range items {
+                       candidate := candidateAny.(string)
                        if reg.MatchString(candidate) {
                                if pattern == constant.IP {
                                        providers, err = 
p.findByAddress(candidate)
diff --git a/pkg/admin/services/provider_service_impl_test.go 
b/pkg/admin/services/provider_service_impl_test.go
index cffa0e00..75117ba8 100644
--- a/pkg/admin/services/provider_service_impl_test.go
+++ b/pkg/admin/services/provider_service_impl_test.go
@@ -22,6 +22,8 @@ import (
        "sync"
        "testing"
 
+       set "github.com/dubbogo/gost/container/set"
+
        "github.com/stretchr/testify/assert"
 
        "github.com/apache/dubbo-admin/pkg/admin/model/util"
@@ -59,12 +61,12 @@ func TestProviderServiceImpl_FindServices(t *testing.T) {
        defer cache.InterfaceRegistryCache.Delete(constant.ProvidersCategory)
        tests := []struct {
                name    string
-               want    []string
+               want    *set.HashSet
                wantErr bool
        }{
                {
                        name:    "Test",
-                       want:    []string{"test"},
+                       want:    set.NewSet("test"),
                        wantErr: false,
                },
        }
@@ -86,12 +88,12 @@ func TestProviderServiceImpl_FindApplications(t *testing.T) 
{
        defer cache.InterfaceRegistryCache.Delete(constant.ProvidersCategory)
        tests := []struct {
                name    string
-               want    []string
+               want    *set.HashSet
                wantErr bool
        }{
                {
                        name:    "Test",
-                       want:    []string{"test"},
+                       want:    set.NewSet("test"),
                        wantErr: false,
                },
        }
@@ -113,12 +115,12 @@ func TestProviderServiceImpl_findAddresses(t *testing.T) {
        defer cache.InterfaceRegistryCache.Delete(constant.ProvidersCategory)
        tests := []struct {
                name    string
-               want    []string
+               want    *set.HashSet
                wantErr bool
        }{
                {
                        name:    "Test",
-                       want:    []string{common.GetLocalIp() + ":0"},
+                       want:    set.NewSet(common.GetLocalIp() + ":0"),
                        wantErr: false,
                },
        }
diff --git a/pkg/admin/services/route_service_impl.go 
b/pkg/admin/services/route_service_impl.go
index 49814ef8..a51c3b44 100644
--- a/pkg/admin/services/route_service_impl.go
+++ b/pkg/admin/services/route_service_impl.go
@@ -21,6 +21,8 @@ import (
        "fmt"
        "strings"
 
+       "github.com/apache/dubbo-admin/pkg/logger"
+
        "github.com/apache/dubbo-admin/pkg/admin/config"
        "github.com/apache/dubbo-admin/pkg/admin/constant"
        "github.com/apache/dubbo-admin/pkg/admin/model"
@@ -268,3 +270,24 @@ func detachId(id string) []string {
                return []string{id}
        }
 }
+
+func GetRules(con string) (map[string]string, error) {
+       list := make(map[string]string)
+       if con == "" || con == "*" {
+               rules, err := config.Governance.GetList("dubbo")
+               if _, ok := err.(*config.RuleNotFound); ok {
+                       logger.Infof("No rule found from config center, err msg 
is %s", err.Error())
+                       return list, nil
+               }
+               list = rules
+       } else {
+               key := GetOverridePath(con)
+               rule, err := config.Governance.GetConfig(key)
+               if _, ok := err.(*config.RuleNotFound); ok {
+                       logger.Infof("No rule found from config center, err msg 
is %s", err.Error())
+                       return list, nil
+               }
+               list[key] = rule
+       }
+       return list, nil
+}
diff --git a/pkg/admin/services/traffic/accesslog.go 
b/pkg/admin/services/traffic/accesslog.go
index 7360b125..d78d839a 100644
--- a/pkg/admin/services/traffic/accesslog.go
+++ b/pkg/admin/services/traffic/accesslog.go
@@ -45,7 +45,7 @@ func (tm *AccesslogService) Delete(a *model.Accesslog) error {
 func (tm *AccesslogService) Search(a *model.Accesslog) ([]*model.Accesslog, 
error) {
        result := make([]*model.Accesslog, 0)
 
-       list, err := getRules(a.Application)
+       list, err := services.GetRules(a.Application)
        if err != nil {
                return result, err
        }
diff --git a/pkg/admin/services/traffic/argument.go 
b/pkg/admin/services/traffic/argument.go
index 2b605e81..0623b64c 100644
--- a/pkg/admin/services/traffic/argument.go
+++ b/pkg/admin/services/traffic/argument.go
@@ -55,7 +55,7 @@ func (tm *ArgumentService) Search(a *model.Argument) 
([]*model.Argument, error)
                con = util.ColonSeparatedKey(a.Service, a.Group, a.Version)
        }
 
-       list, err := getRules(con)
+       list, err := services.GetRules(con)
        if err != nil {
                return result, err
        }
diff --git a/pkg/admin/services/traffic/generic_rule_operation.go 
b/pkg/admin/services/traffic/generic_rule_operation.go
index 2f233579..90228d61 100644
--- a/pkg/admin/services/traffic/generic_rule_operation.go
+++ b/pkg/admin/services/traffic/generic_rule_operation.go
@@ -20,7 +20,6 @@ package traffic
 import (
        "github.com/apache/dubbo-admin/pkg/admin/config"
        "github.com/apache/dubbo-admin/pkg/admin/model"
-       "github.com/apache/dubbo-admin/pkg/admin/services"
        "github.com/apache/dubbo-admin/pkg/logger"
        perrors "github.com/pkg/errors"
        "gopkg.in/yaml.v2"
@@ -174,27 +173,6 @@ func getValue(rawRule, side, param string) (interface{}, 
error) {
        return "", nil
 }
 
-func getRules(con string) (map[string]string, error) {
-       list := make(map[string]string)
-       if con == "" || con == "*" {
-               rules, err := config.Governance.GetList("dubbo")
-               if _, ok := err.(*config.RuleNotFound); ok {
-                       logger.Infof("No rule found from config center, err msg 
is %s", err.Error())
-                       return list, nil
-               }
-               list = rules
-       } else {
-               key := services.GetOverridePath(con)
-               rule, err := config.Governance.GetConfig(key)
-               if _, ok := err.(*config.RuleNotFound); ok {
-                       logger.Infof("No rule found from config center, err msg 
is %s", err.Error())
-                       return list, nil
-               }
-               list[key] = rule
-       }
-       return list, nil
-}
-
 func createOrUpdateCondition(key string, newRule model.ConditionRoute) error {
        var mergedRule string
        newRuleByte, _ := yaml.Marshal(newRule)
diff --git a/pkg/admin/services/traffic/gray.go 
b/pkg/admin/services/traffic/gray.go
index 4e77424f..c8235666 100644
--- a/pkg/admin/services/traffic/gray.go
+++ b/pkg/admin/services/traffic/gray.go
@@ -47,7 +47,7 @@ func (tm *GrayService) Delete(g *model.Gray) error {
 func (tm *GrayService) Search(g *model.Gray) ([]*model.Gray, error) {
        result := make([]*model.Gray, 0)
 
-       list, err := getRules(g.Application)
+       list, err := services.GetRules(g.Application)
        if err != nil {
                return result, err
        }
diff --git a/pkg/admin/services/traffic/mock.go 
b/pkg/admin/services/traffic/mock.go
index a7dc5069..18de8e72 100644
--- a/pkg/admin/services/traffic/mock.go
+++ b/pkg/admin/services/traffic/mock.go
@@ -53,7 +53,7 @@ func (tm *MockService) Search(m *model.Mock) ([]*model.Mock, 
error) {
        if m.Service != "" {
                con = util.ColonSeparatedKey(m.Service, m.Group, m.Version)
        }
-       list, err := getRules(con)
+       list, err := services.GetRules(con)
        if err != nil {
                return result, err
        }
diff --git a/pkg/admin/services/traffic/region.go 
b/pkg/admin/services/traffic/region.go
index d2ee324a..95f0032e 100644
--- a/pkg/admin/services/traffic/region.go
+++ b/pkg/admin/services/traffic/region.go
@@ -55,7 +55,7 @@ func (tm *RegionService) Search(r *model.Region) 
([]*model.Region, error) {
                con = util.ColonSeparatedKey(r.Service, r.Group, r.Version)
        }
 
-       list, err := getRules(con)
+       list, err := services.GetRules(con)
        if err != nil {
                return result, err
        }
diff --git a/pkg/admin/services/traffic/retry.go 
b/pkg/admin/services/traffic/retry.go
index 0909f6ee..5b663b33 100644
--- a/pkg/admin/services/traffic/retry.go
+++ b/pkg/admin/services/traffic/retry.go
@@ -54,7 +54,7 @@ func (tm *RetryService) Search(r *model.Retry) 
([]*model.Retry, error) {
                con = util.ColonSeparatedKey(r.Service, r.Group, r.Version)
        }
 
-       list, err := getRules(con)
+       list, err := services.GetRules(con)
        if err != nil {
                return result, err
        }
diff --git a/pkg/admin/services/traffic/timeout.go 
b/pkg/admin/services/traffic/timeout.go
index eeca1ef4..91b22bd3 100644
--- a/pkg/admin/services/traffic/timeout.go
+++ b/pkg/admin/services/traffic/timeout.go
@@ -54,7 +54,7 @@ func (tm *TimeoutService) Search(t *model.Timeout) 
([]*model.Timeout, error) {
                con = util.ColonSeparatedKey(t.Service, t.Group, t.Version)
        }
 
-       list, err := getRules(con)
+       list, err := services.GetRules(con)
        if err != nil {
                return result, err
        }
diff --git a/pkg/admin/services/traffic/weight.go 
b/pkg/admin/services/traffic/weight.go
index e2f933fc..276ac71b 100644
--- a/pkg/admin/services/traffic/weight.go
+++ b/pkg/admin/services/traffic/weight.go
@@ -55,7 +55,7 @@ func (tm *WeightService) Search(p *model.Percentage) 
([]*model.Percentage, error
                con = util.ColonSeparatedKey(p.Service, p.Group, p.Version)
        }
 
-       list, err := getRules(con)
+       list, err := services.GetRules(con)
        if err != nil {
                return result, err
        }

Reply via email to