dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/client/resource/unmarshal_eds.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 resource
    25  
    26  import (
    27  	"fmt"
    28  	"net"
    29  	"strconv"
    30  )
    31  
    32  import (
    33  	dubbogoLogger "github.com/dubbogo/gost/log/logger"
    34  
    35  	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    36  	v3endpointpb "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
    37  	v3typepb "github.com/envoyproxy/go-control-plane/envoy/type/v3"
    38  
    39  	"github.com/golang/protobuf/proto"
    40  
    41  	"google.golang.org/protobuf/types/known/anypb"
    42  )
    43  
    44  import (
    45  	"dubbo.apache.org/dubbo-go/v3/xds/utils/pretty"
    46  )
    47  
    48  // UnmarshalEndpoints processes resources received in an EDS response,
    49  // validates them, and transforms them into a native struct which contains only
    50  // fields we are interested in.
    51  func UnmarshalEndpoints(opts *UnmarshalOptions) (map[string]EndpointsUpdateErrTuple, UpdateMetadata, error) {
    52  	update := make(map[string]EndpointsUpdateErrTuple)
    53  	md, err := processAllResources(opts, update)
    54  	return update, md, err
    55  }
    56  
    57  func unmarshalEndpointsResource(r *anypb.Any, logger dubbogoLogger.Logger) (string, EndpointsUpdate, error) {
    58  	if !IsEndpointsResource(r.GetTypeUrl()) {
    59  		return "", EndpointsUpdate{}, fmt.Errorf("unexpected resource type: %q ", r.GetTypeUrl())
    60  	}
    61  
    62  	cla := &v3endpointpb.ClusterLoadAssignment{}
    63  	if err := proto.Unmarshal(r.GetValue(), cla); err != nil {
    64  		return "", EndpointsUpdate{}, fmt.Errorf("failed to unmarshal resource: %v", err)
    65  	}
    66  	dubbogoLogger.Debugf("Resource with name: %v, type: %T, contains: %v", cla.GetClusterName(), cla, pretty.ToJSON(cla))
    67  
    68  	u, err := parseEDSRespProto(cla)
    69  	if err != nil {
    70  		return cla.GetClusterName(), EndpointsUpdate{}, err
    71  	}
    72  	u.Raw = r
    73  	return cla.GetClusterName(), u, nil
    74  }
    75  
    76  func parseAddress(socketAddress *v3corepb.SocketAddress) string {
    77  	return net.JoinHostPort(socketAddress.GetAddress(), strconv.Itoa(int(socketAddress.GetPortValue())))
    78  }
    79  
    80  func parseDropPolicy(dropPolicy *v3endpointpb.ClusterLoadAssignment_Policy_DropOverload) OverloadDropConfig {
    81  	percentage := dropPolicy.GetDropPercentage()
    82  	var (
    83  		numerator   = percentage.GetNumerator()
    84  		denominator uint32
    85  	)
    86  	switch percentage.GetDenominator() {
    87  	case v3typepb.FractionalPercent_HUNDRED:
    88  		denominator = 100
    89  	case v3typepb.FractionalPercent_TEN_THOUSAND:
    90  		denominator = 10000
    91  	case v3typepb.FractionalPercent_MILLION:
    92  		denominator = 1000000
    93  	}
    94  	return OverloadDropConfig{
    95  		Category:    dropPolicy.GetCategory(),
    96  		Numerator:   numerator,
    97  		Denominator: denominator,
    98  	}
    99  }
   100  
   101  func parseEndpoints(lbEndpoints []*v3endpointpb.LbEndpoint) []Endpoint {
   102  	endpoints := make([]Endpoint, 0, len(lbEndpoints))
   103  	for _, lbEndpoint := range lbEndpoints {
   104  		endpoints = append(endpoints, Endpoint{
   105  			HealthStatus: EndpointHealthStatus(lbEndpoint.GetHealthStatus()),
   106  			Address:      parseAddress(lbEndpoint.GetEndpoint().GetAddress().GetSocketAddress()),
   107  			Weight:       lbEndpoint.GetLoadBalancingWeight().GetValue(),
   108  		})
   109  	}
   110  	return endpoints
   111  }
   112  
   113  func parseEDSRespProto(m *v3endpointpb.ClusterLoadAssignment) (EndpointsUpdate, error) {
   114  	ret := EndpointsUpdate{}
   115  	for _, dropPolicy := range m.GetPolicy().GetDropOverloads() {
   116  		ret.Drops = append(ret.Drops, parseDropPolicy(dropPolicy))
   117  	}
   118  	priorities := make(map[uint32]struct{})
   119  	for _, locality := range m.Endpoints {
   120  		l := locality.GetLocality()
   121  		if l == nil {
   122  			return EndpointsUpdate{}, fmt.Errorf("EDS response contains a locality without ID, locality: %+v", locality)
   123  		}
   124  		lid := LocalityID{
   125  			Region:  l.Region,
   126  			Zone:    l.Zone,
   127  			SubZone: l.SubZone,
   128  		}
   129  		priority := locality.GetPriority()
   130  		priorities[priority] = struct{}{}
   131  		ret.Localities = append(ret.Localities, Locality{
   132  			ID:        lid,
   133  			Endpoints: parseEndpoints(locality.GetLbEndpoints()),
   134  			Weight:    locality.GetLoadBalancingWeight().GetValue(),
   135  			Priority:  priority,
   136  		})
   137  	}
   138  	for i := 0; i < len(priorities); i++ {
   139  		if _, ok := priorities[uint32(i)]; !ok {
   140  			return EndpointsUpdate{}, fmt.Errorf("priority %v missing (with different priorities %v received)", i, priorities)
   141  		}
   142  	}
   143  	return ret, nil
   144  }