server/service/govern/view.go (291 lines of code) (raw):
/*
* 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 govern
import (
"context"
"fmt"
"github.com/apache/servicecomb-service-center/datasource"
"github.com/apache/servicecomb-service-center/pkg/log"
"github.com/apache/servicecomb-service-center/pkg/util"
"github.com/apache/servicecomb-service-center/server/config"
discosvc "github.com/apache/servicecomb-service-center/server/service/disco"
"github.com/apache/servicecomb-service-center/server/service/validator"
pb "github.com/go-chassis/cari/discovery"
"github.com/jinzhu/copier"
)
var defaultOptions = []string{"tags", "instances", "schemas", "dependencies"}
type ServiceDetailOpt struct {
domainProject string
service *pb.MicroService
countOnly bool
options []string
}
func ListServiceDetail(ctx context.Context, in *pb.GetServicesInfoRequest) (*pb.GetServicesInfoResponse, error) {
ctx = util.WithCacheOnly(ctx)
optionMap := make(map[string]struct{}, len(in.Options))
for _, opt := range in.Options {
optionMap[opt] = struct{}{}
}
options := make([]string, 0, len(optionMap))
if _, ok := optionMap["all"]; ok {
optionMap["statistics"] = struct{}{}
options = defaultOptions
} else {
for opt := range optionMap {
options = append(options, opt)
}
}
var st *pb.Statistics
if _, ok := optionMap["statistics"]; ok {
var err error
st, err = datasource.GetMetadataManager().Statistics(ctx, in.WithShared)
if err != nil {
return nil, pb.NewError(pb.ErrInternal, err.Error())
}
if len(optionMap) == 1 {
return &pb.GetServicesInfoResponse{
Statistics: st,
}, nil
}
}
//获取所有服务
resp, err := discosvc.ListService(ctx, &pb.GetServicesRequest{WithShared: in.WithShared})
if err != nil {
log.Error("get all services by domain failed", err)
return nil, pb.NewError(pb.ErrInternal, err.Error())
}
services := resp.Services
allServiceDetails := make([]*pb.ServiceDetail, 0, len(services))
domainProject := util.ParseDomainProject(ctx)
instanceProperties := config.GetStringMap("registry.instance.properties")
for _, service := range services {
if !filterServices(domainProject, in, service) {
continue
}
serviceDetail, err := getServiceDetailUtil(ctx, ServiceDetailOpt{
domainProject: domainProject,
service: service,
countOnly: in.CountOnly,
options: options,
})
if err != nil {
return nil, pb.NewError(pb.ErrInternal, err.Error())
}
serviceDetail.MicroService = service
tmpServiceDetail, err := NewServiceOverview(serviceDetail, instanceProperties)
if err != nil {
return nil, err
}
allServiceDetails = append(allServiceDetails, tmpServiceDetail)
}
return &pb.GetServicesInfoResponse{
AllServicesDetail: allServiceDetails,
Statistics: st,
}, nil
}
func getServiceDetailUtil(ctx context.Context, opts ServiceDetailOpt) (*pb.ServiceDetail, error) {
service := opts.service
serviceID := service.ServiceId
serviceLogName := fmt.Sprintf("%s][%s/%s/%s/%s", service.ServiceId, service.Environment, service.AppId, service.ServiceName, service.Version)
options := opts.options
serviceDetail := new(pb.ServiceDetail)
if opts.countOnly {
serviceDetail.Statics = new(pb.Statistics)
}
for _, opt := range options {
expr := opt
switch expr {
case "tags":
resp, err := discosvc.ListTag(ctx, &pb.GetServiceTagsRequest{
ServiceId: serviceID,
})
if err != nil {
log.Error(fmt.Sprintf("get service[%s]'s all tags failed", serviceLogName), err)
return nil, err
}
serviceDetail.Tags = resp.Tags
case "instances":
resp, err := discosvc.ListInstance(ctx, &pb.GetInstancesRequest{
ProviderServiceId: serviceID,
})
if err != nil {
log.Error(fmt.Sprintf("get service[%s]'s all instances failed", serviceLogName), err)
return nil, err
}
if opts.countOnly {
if err != nil {
log.Error(fmt.Sprintf("get number of service[%s]'s instances failed", serviceLogName), err)
return nil, err
}
serviceDetail.Statics.Instances = &pb.StInstance{
Count: int64(len(resp.Instances)),
}
continue
}
serviceDetail.Instances = resp.Instances
case "schemas":
schemas, err := discosvc.ListSchema(ctx, &pb.GetAllSchemaRequest{
ServiceId: serviceID,
WithSchema: true,
})
if err != nil {
log.Error(fmt.Sprintf("get service[%s]'s all schemas failed", serviceLogName), err)
return nil, err
}
serviceDetail.SchemaInfos = schemas
case "dependencies":
consumerResp, err := discosvc.ListConsumers(ctx, &pb.GetDependenciesRequest{
ServiceId: serviceID,
NoSelf: true,
SameDomain: true,
})
if err != nil {
log.Error(fmt.Sprintf("get service[%s]'s all consumers failed", serviceLogName), err)
return nil, err
}
providerResp, err := discosvc.ListProviders(ctx, &pb.GetDependenciesRequest{
ServiceId: serviceID,
NoSelf: true,
SameDomain: true,
})
if err != nil {
log.Error(fmt.Sprintf("get service[%s]'s all providers failed", serviceLogName), err)
return nil, err
}
serviceDetail.Consumers = consumerResp.Consumers
serviceDetail.Providers = providerResp.Providers
case "":
continue
default:
log.Error(fmt.Sprintf("request option[%s] is invalid", opt), nil)
}
}
return serviceDetail, nil
}
func filterServices(domainProject string, request *pb.GetServicesInfoRequest, service *pb.MicroService) bool {
if !request.WithShared && datasource.IsGlobal(pb.MicroServiceToKey(domainProject, service)) {
return false
}
if len(request.Environment) > 0 && request.Environment != service.Environment {
return false
}
if len(request.AppId) > 0 && request.AppId != service.AppId {
return false
}
if len(request.ServiceName) > 0 && request.ServiceName != service.ServiceName {
return false
}
if len(request.Properties) > 0 && !matchAllProperties(request.Properties, service) {
return false
}
return true
}
func matchAllProperties(properties map[string]string, service *pb.MicroService) bool {
for k, v := range properties {
val, ok := service.Properties[k]
if !ok || v != val {
return false
}
}
return true
}
func NewServiceOverview(serviceDetail *pb.ServiceDetail, innerProperties map[string]string) (*pb.ServiceDetail, error) {
tmpServiceDetail := &pb.ServiceDetail{}
err := copier.CopyWithOption(tmpServiceDetail, serviceDetail, copier.Option{DeepCopy: true})
if err != nil {
return nil, pb.NewError(pb.ErrInternal, err.Error())
}
tmpServiceDetail.MicroService.Schemas = nil
instances := tmpServiceDetail.Instances
for _, instance := range instances {
instance.Properties = removeCustomProperties(instance.Properties, innerProperties)
}
return tmpServiceDetail, nil
}
func removeCustomProperties(properties, innerProperties map[string]string) map[string]string {
if len(innerProperties) == 0 {
return nil
}
props := make(map[string]string)
for k, v := range properties {
if _, ok := innerProperties[k]; ok {
props[k] = v
}
}
return props
}
func GetServiceDetail(ctx context.Context, in *pb.GetServiceRequest) (*pb.ServiceDetail, error) {
ctx = util.WithCacheOnly(ctx)
serviceID := in.ServiceId
if len(serviceID) == 0 {
return nil, pb.NewError(pb.ErrInvalidParams, "Invalid request for getting service detail.")
}
service, err := discosvc.GetService(ctx, in)
if err != nil {
log.Error(fmt.Sprintf("get service[%s] failed", serviceID), err)
return nil, err
}
serviceInfo := new(pb.ServiceDetail)
serviceInfo.MicroService = service
key := &pb.MicroServiceKey{
Environment: service.Environment,
AppId: service.AppId,
ServiceName: service.ServiceName,
}
versions, err := getServiceAllVersions(ctx, key)
if err != nil {
log.Error(fmt.Sprintf("get service[%s/%s/%s] all versions failed",
service.Environment, service.AppId, service.ServiceName), err)
return nil, pb.NewError(pb.ErrInternal, err.Error())
}
serviceInfo.MicroServiceVersions = versions
tagsResp, err := discosvc.ListTag(ctx, &pb.GetServiceTagsRequest{ServiceId: serviceID})
if err != nil {
log.Error(fmt.Sprintf("get service[%s] tags failed", serviceID), err)
return nil, err
}
serviceInfo.Tags = tagsResp.Tags
schemas, err := discosvc.ListSchema(ctx, &pb.GetAllSchemaRequest{ServiceId: serviceID, WithSchema: true})
if err != nil {
log.Error(fmt.Sprintf("get service[%s] schemas failed", serviceID), err)
return nil, err
}
serviceInfo.SchemaInfos = schemas
providerResp, err := discosvc.ListProviders(ctx, &pb.GetDependenciesRequest{ServiceId: serviceID})
if err != nil {
log.Error(fmt.Sprintf("get service[%s] providers failed", serviceID), err)
return nil, err
}
serviceInfo.Providers = providerResp.Providers
consumerResp, err := discosvc.ListConsumers(ctx, &pb.GetDependenciesRequest{ServiceId: serviceID})
if err != nil {
log.Error(fmt.Sprintf("get service[%s] consumers failed", serviceID), err)
return nil, err
}
serviceInfo.Consumers = consumerResp.Consumers
instResp, err := discosvc.ListInstance(ctx, &pb.GetInstancesRequest{ProviderServiceId: serviceID})
if err != nil {
log.Error(fmt.Sprintf("get service[%s] instances failed", serviceID), err)
return nil, err
}
serviceInfo.Instances = instResp.Instances
return serviceInfo, nil
}
func getServiceAllVersions(ctx context.Context, key *pb.MicroServiceKey) ([]string, error) {
resp, err := discosvc.FindService(ctx, key)
if err != nil {
return nil, err
}
versions := make([]string, 0, len(resp.Services))
for _, svc := range resp.Services {
versions = append(versions, svc.Version)
}
return versions, nil
}
func ListApp(ctx context.Context, in *pb.GetAppsRequest) (*pb.GetAppsResponse, error) {
if err := validator.ValidateGetAppsRequest(in); err != nil {
return nil, pb.NewError(pb.ErrInvalidParams, err.Error())
}
return datasource.GetMetadataManager().ListApp(ctx, in)
}
func GetOverview(ctx context.Context, in *pb.GetServicesRequest) (*pb.Statistics, error) {
ctx = util.WithCacheOnly(ctx)
return datasource.GetMetadataManager().GetOverview(ctx, in)
}