dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/csds/csds.go (about) 1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /* 19 * 20 * Copyright 2021 gRPC authors. 21 * 22 */ 23 24 // Package csds implements features to dump the status (xDS responses) the 25 // xds_client is using. 26 // 27 // Notice: This package is EXPERIMENTAL and may be changed or removed in a later 28 // release. 29 package csds 30 31 import ( 32 "context" 33 "io" 34 ) 35 36 import ( 37 "github.com/dubbogo/gost/log/logger" 38 39 v3adminpb "github.com/envoyproxy/go-control-plane/envoy/admin/v3" 40 v2corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" 41 v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 42 v3statuspb "github.com/envoyproxy/go-control-plane/envoy/service/status/v3" 43 44 "github.com/golang/protobuf/proto" 45 46 "google.golang.org/grpc/codes" 47 48 "google.golang.org/grpc/status" 49 50 "google.golang.org/protobuf/types/known/timestamppb" 51 ) 52 53 import ( 54 "dubbo.apache.org/dubbo-go/v3/xds/client" 55 _ "dubbo.apache.org/dubbo-go/v3/xds/client/controller/version/v2" 56 _ "dubbo.apache.org/dubbo-go/v3/xds/client/controller/version/v3" 57 "dubbo.apache.org/dubbo-go/v3/xds/client/resource" 58 ) 59 60 // Register v3 xds_client. 61 var ( 62 newXDSClient = func() client.XDSClient { 63 c, err := client.New() 64 if err != nil { 65 logger.Warnf("failed to create xds client: %v", err) 66 return nil 67 } 68 return c 69 } 70 ) 71 72 const ( 73 listenerTypeURL = "envoy.config.listener.v3.Listener" 74 routeConfigTypeURL = "envoy.config.route.v3.RouteConfiguration" 75 clusterTypeURL = "envoy.config.cluster.v3.Cluster" 76 endpointsTypeURL = "envoy.config.endpoint.v3.ClusterLoadAssignment" 77 ) 78 79 // ClientStatusDiscoveryServer implementations interface ClientStatusDiscoveryServiceServer. 80 type ClientStatusDiscoveryServer struct { 81 // xdsClient will always be the same in practice. But we keep a copy in each 82 // server instance for testing. 83 xdsClient client.XDSClient 84 } 85 86 // NewClientStatusDiscoveryServer returns an implementation of the CSDS server that can be 87 // registered on a gRPC server. 88 func NewClientStatusDiscoveryServer() (*ClientStatusDiscoveryServer, error) { 89 return &ClientStatusDiscoveryServer{xdsClient: newXDSClient()}, nil 90 } 91 92 // StreamClientStatus implementations interface ClientStatusDiscoveryServiceServer. 93 func (s *ClientStatusDiscoveryServer) StreamClientStatus(stream v3statuspb.ClientStatusDiscoveryService_StreamClientStatusServer) error { 94 for { 95 req, err := stream.Recv() 96 if err == io.EOF { 97 return nil 98 } 99 if err != nil { 100 return err 101 } 102 resp, err := s.buildClientStatusRespForReq(req) 103 if err != nil { 104 return err 105 } 106 if err := stream.Send(resp); err != nil { 107 return err 108 } 109 } 110 } 111 112 // FetchClientStatus implementations interface ClientStatusDiscoveryServiceServer. 113 func (s *ClientStatusDiscoveryServer) FetchClientStatus(_ context.Context, req *v3statuspb.ClientStatusRequest) (*v3statuspb.ClientStatusResponse, error) { 114 return s.buildClientStatusRespForReq(req) 115 } 116 117 // buildClientStatusRespForReq fetches the status from the client, and returns 118 // the response to be sent back to xdsclient. 119 // 120 // If it returns an error, the error is a status error. 121 func (s *ClientStatusDiscoveryServer) buildClientStatusRespForReq(req *v3statuspb.ClientStatusRequest) (*v3statuspb.ClientStatusResponse, error) { 122 if s.xdsClient == nil { 123 return &v3statuspb.ClientStatusResponse{}, nil 124 } 125 // Field NodeMatchers is unsupported, by design 126 // https://github.com/grpc/proposal/blob/master/A40-csds-support.md#detail-node-matching. 127 if len(req.NodeMatchers) != 0 { 128 return nil, status.Errorf(codes.InvalidArgument, "node_matchers are not supported, request contains node_matchers: %v", req.NodeMatchers) 129 } 130 131 lds := dumpToGenericXdsConfig(listenerTypeURL, s.xdsClient.DumpLDS) 132 rds := dumpToGenericXdsConfig(routeConfigTypeURL, s.xdsClient.DumpRDS) 133 cds := dumpToGenericXdsConfig(clusterTypeURL, s.xdsClient.DumpCDS) 134 eds := dumpToGenericXdsConfig(endpointsTypeURL, s.xdsClient.DumpEDS) 135 configs := make([]*v3statuspb.ClientConfig_GenericXdsConfig, 0, len(lds)+len(rds)+len(cds)+len(eds)) 136 configs = append(configs, lds...) 137 configs = append(configs, rds...) 138 configs = append(configs, cds...) 139 configs = append(configs, eds...) 140 141 ret := &v3statuspb.ClientStatusResponse{ 142 Config: []*v3statuspb.ClientConfig{ 143 { 144 Node: nodeProtoToV3(s.xdsClient.BootstrapConfig().XDSServer.NodeProto), 145 GenericXdsConfigs: configs, 146 }, 147 }, 148 } 149 return ret, nil 150 } 151 152 // Close cleans up the resources. 153 func (s *ClientStatusDiscoveryServer) Close() { 154 if s.xdsClient != nil { 155 s.xdsClient.Close() 156 } 157 } 158 159 // nodeProtoToV3 converts the given proto into a v3.Node. n is from bootstrap 160 // config, it can be either v2.Node or v3.Node. 161 // 162 // If n is already a v3.Node, return it. 163 // If n is v2.Node, marshal and unmarshal it to v3. 164 // Otherwise, return nil. 165 // 166 // The default case (not v2 or v3) is nil, instead of error, because the 167 // resources in the response are more important than the node. The worst case is 168 // that the user will receive no Node info, but will still get resources. 169 func nodeProtoToV3(n proto.Message) *v3corepb.Node { 170 var node *v3corepb.Node 171 switch nn := n.(type) { 172 case *v3corepb.Node: 173 node = nn 174 case *v2corepb.Node: 175 v2, err := proto.Marshal(nn) 176 if err != nil { 177 logger.Warnf("Failed to marshal node (%v): %v", n, err) 178 break 179 } 180 node = new(v3corepb.Node) 181 if err := proto.Unmarshal(v2, node); err != nil { 182 logger.Warnf("Failed to unmarshal node (%v): %v", v2, err) 183 } 184 default: 185 logger.Warnf("node from bootstrap is %#v, only v2.Node and v3.Node are supported", nn) 186 } 187 return node 188 } 189 190 func dumpToGenericXdsConfig(typeURL string, dumpF func() map[string]resource.UpdateWithMD) []*v3statuspb.ClientConfig_GenericXdsConfig { 191 dump := dumpF() 192 ret := make([]*v3statuspb.ClientConfig_GenericXdsConfig, 0, len(dump)) 193 for name, d := range dump { 194 config := &v3statuspb.ClientConfig_GenericXdsConfig{ 195 TypeUrl: typeURL, 196 Name: name, 197 VersionInfo: d.MD.Version, 198 XdsConfig: d.Raw, 199 LastUpdated: timestamppb.New(d.MD.Timestamp), 200 ClientStatus: serviceStatusToProto(d.MD.Status), 201 } 202 if errState := d.MD.ErrState; errState != nil { 203 config.ErrorState = &v3adminpb.UpdateFailureState{ 204 LastUpdateAttempt: timestamppb.New(errState.Timestamp), 205 Details: errState.Err.Error(), 206 VersionInfo: errState.Version, 207 } 208 } 209 ret = append(ret, config) 210 } 211 return ret 212 } 213 214 func serviceStatusToProto(serviceStatus resource.ServiceStatus) v3adminpb.ClientResourceStatus { 215 switch serviceStatus { 216 case resource.ServiceStatusUnknown: 217 return v3adminpb.ClientResourceStatus_UNKNOWN 218 case resource.ServiceStatusRequested: 219 return v3adminpb.ClientResourceStatus_REQUESTED 220 case resource.ServiceStatusNotExist: 221 return v3adminpb.ClientResourceStatus_DOES_NOT_EXIST 222 case resource.ServiceStatusACKed: 223 return v3adminpb.ClientResourceStatus_ACKED 224 case resource.ServiceStatusNACKed: 225 return v3adminpb.ClientResourceStatus_NACKED 226 default: 227 return v3adminpb.ClientResourceStatus_UNKNOWN 228 } 229 }