blob: 6268a759acdc8b8baa7e11fe4421f07bdbab0f2d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package handlers
import (
"encoding/json"
"net/http"
"strconv"
"time"
"dubbo.apache.org/dubbo-go/v3/config/generic"
hessian "github.com/apache/dubbo-go-hessian2"
"github.com/apache/dubbo-kubernetes/pkg/core/cmd/version"
"github.com/apache/dubbo-kubernetes/pkg/core/logger"
"dubbo.apache.org/dubbo-go/v3/metadata/definition"
"github.com/apache/dubbo-kubernetes/pkg/admin/constant"
"github.com/vcraescu/go-paginator"
"github.com/vcraescu/go-paginator/adapter"
"dubbo.apache.org/dubbo-go/v3/metadata/identifier"
"github.com/apache/dubbo-kubernetes/pkg/admin/config"
"github.com/apache/dubbo-kubernetes/pkg/admin/model"
"github.com/apache/dubbo-kubernetes/pkg/admin/util"
"github.com/apache/dubbo-kubernetes/pkg/admin/services"
"github.com/gin-gonic/gin"
)
var (
providerService services.ProviderService = &services.ProviderServiceImpl{}
consumerService services.ConsumerService = &services.ConsumerServiceImpl{}
monitorService services.MonitorService = &services.PrometheusServiceImpl{}
genericServiceImpl *services.GenericServiceImpl = &services.GenericServiceImpl{}
serviceTesting *services.ServiceTestingV3 = &services.ServiceTestingV3{}
)
// AllServices get all services
// @Summary Get all services
// @Description Get all services
// @Tags Services
// @Accept json
// @Produce json
// @Param env path string false "environment" default(dev)
// @Success 200 {object} []string
// @Failure 500 {object} model.HTTPError
// @Router /api/{env}/services [get]
func AllServices(c *gin.Context) {
allServices, err := providerService.FindServices()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, allServices)
}
// SearchService search services by different patterns and keywords
// @Summary Search services by different patterns and keywords
// @Description Search services by different patterns and keywords
// @Tags Services
// @Accept json
// @Produce json
// @Param env path string false "environment" default(dev)
// @Param pattern query string true "supported values: application, service or ip"
// @Param filter query string true "keyword to search"
// @Param page query string false "page number"
// @Param size query string false "page size"
// @Success 200 {object} model.ListServiceByPage
// @Failure 500 {object} model.HTTPError
// @Router /api/{env}/service [get]
func SearchService(c *gin.Context) {
pattern := c.Query("pattern")
filter := c.Query("filter")
page := c.Query("page")
pageInt, err := strconv.Atoi(page)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
size := c.Query("size")
sizeInt, err := strconv.Atoi(page)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
// get services
serviceDTOS, err := providerService.FindService(pattern, filter)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
// paging
p := paginator.New(adapter.NewSliceAdapter(serviceDTOS), sizeInt)
p.SetPage(pageInt)
var serviceResults []*model.ServiceDTO
p.Results(&serviceResults)
// return results
c.JSON(http.StatusOK, model.ListServiceByPage{
Content: serviceResults,
TotalPages: p.PageNums(),
TotalElements: p.Nums(),
Size: size,
First: pageInt == 0,
Last: pageInt == p.PageNums()-1,
PageNumber: page,
Offset: (pageInt - 1) * sizeInt,
})
}
// AllApplications get all applications
// @Summary Get all applications
// @Description Get all applications
// @Tags Services
// @Accept json
// @Produce json
// @Param env path string false "environment" default(dev)
// @Success 200 {object} []string
// @Failure 500 {object} model.HTTPError
// @Router /api/{env}/applications [get]
func AllApplications(c *gin.Context) {
applications, err := providerService.FindApplications()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, applications)
}
// AllConsumers get all consumers
// @Summary Get all consumers
// @Description Get all consumers
// @Tags Services
// @Accept json
// @Produce json
// @Param env path string false "environment" default(dev)
// @Success 200 {object} []string
// @Failure 500 {object} model.HTTPError
// @Router /api/{env}/consumers [get]
func AllConsumers(c *gin.Context) {
consumers, err := consumerService.FindAll()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, consumers)
}
// ServiceDetail show detail of the specified service
// @Summary Show detail of the specified service
// @Description Show detail of the specified service
// @Tags Services
// @Accept json
// @Produce json
// @Param env path string false "environment" default(dev)
// @Param service path string true "service format: 'group/service:version'"
// @Success 200 {object} model.ServiceDetailDTO
// @Failure 500 {object} model.HTTPError
// @Router /api/{env}/service/{service} [get]
func ServiceDetail(c *gin.Context) {
service := c.Param("service")
group := util.GetGroup(service)
version := util.GetVersion(service)
interfaze := util.GetInterface(service)
providers, err := providerService.FindByService(service)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
consumers, err := consumerService.FindByService(service)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
application := ""
if len(providers) > 0 {
application = providers[0].Application
}
identifier := &identifier.MetadataIdentifier{
Application: application,
BaseMetadataIdentifier: identifier.BaseMetadataIdentifier{
ServiceInterface: interfaze,
Version: version,
Group: group,
Side: constant.ProviderSide,
},
}
metadata, _ := config.MetadataReportCenter.GetServiceDefinition(identifier)
typed_meta := definition.ServiceDefinition{}
err = json.Unmarshal([]byte(metadata), &typed_meta)
if err != nil {
logger.Errorf("Error parsing metadata, err msg is %s", err.Error())
}
serviceDetail := &model.ServiceDetailDTO{
Providers: providers,
Consumers: consumers,
Service: service,
Application: application,
Metadata: typed_meta,
}
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 := 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(),
})
}
c.JSON(http.StatusOK, res)
}
// 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.Metadata()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
}
c.JSON(http.StatusOK, res)
}
// PromDiscovery expose the interface of Prometheus http_sd service discovery.
// @Summary expose the interface of Prometheus http_sd service discovery.
// @Description expose the interface of Prometheus http_sd service discovery.
// @Tags metrics
// @Accept json
// @Produce json
// @Success 200 {object} []model.Target
// @Failure 500 {object} model.HTTPError
// @Router /api/{env}/metrics/prometheus [get]
func PromDiscovery(c *gin.Context) {
targets, err := monitorService.PromDiscovery(c.Writer)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
}
c.JSON(http.StatusOK, targets)
}
// Test works for dubbo2 tcp protocol
func Test(c *gin.Context) {
var serviceTestDTO model.ServiceTest
err := c.BindJSON(&serviceTestDTO)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
refConf := genericServiceImpl.NewRefConf("dubbo-admin", serviceTestDTO.Service, "dubbo")
time.Sleep(2 * time.Second)
resp, err := refConf.
GetRPCService().(*generic.GenericService).
Invoke(
c,
serviceTestDTO.Method,
serviceTestDTO.ParameterTypes,
[]hessian.Object{"A003"}, // fixme
)
refConf.GetInvoker().Destroy()
if err != nil {
logger.Error("Error do generic invoke for service test", err)
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, resp)
}
// HttpTest works for triple protocol
func HttpTest(c *gin.Context) {
// pattern := c.Query("service")
// filter := c.Query("method")
// address := c.Query("address")
// send standard http request to backend http://address/service/method content-type:json
c.JSON(http.StatusOK, gin.H{
"code": 1,
"data": "implement me",
})
}
func MethodDetail(c *gin.Context) {
service := c.Query("service")
group := util.GetGroup(service)
version := util.GetVersion(service)
interfaze := util.GetInterface(service)
application := c.Query("application")
method := c.Query("method")
identifier := &identifier.MetadataIdentifier{
Application: application,
BaseMetadataIdentifier: identifier.BaseMetadataIdentifier{
ServiceInterface: interfaze,
Version: version,
Group: group,
Side: constant.ProviderSide,
},
}
metadata, _ := config.MetadataReportCenter.GetServiceDefinition(identifier)
var methodMetadata model.MethodMetadata
if metadata != "" {
serviceDefinition := &definition.FullServiceDefinition{}
err := json.Unmarshal([]byte(metadata), &serviceDefinition)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
methods := serviceDefinition.Methods
if methods != nil {
for _, m := range methods {
if serviceTesting.SameMethod(m, method) {
methodMetadata = serviceTesting.GenerateMethodMeta(*serviceDefinition, m)
break
}
}
}
}
c.JSON(http.StatusOK, methodMetadata)
}