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) }