github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/xds/internal/xdsclient/xdsresource/unmarshal.go (about)

     1  /*
     2   *
     3   * Copyright 2021 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * 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  // Package xdsresource contains functions to proto xds updates (unmarshal from
    19  // proto), and types for the resource updates.
    20  package xdsresource
    21  
    22  import (
    23  	"errors"
    24  	"fmt"
    25  	"strings"
    26  	"time"
    27  
    28  	"github.com/hxx258456/ccgo/grpc/internal/grpclog"
    29  	"google.golang.org/protobuf/types/known/anypb"
    30  )
    31  
    32  // UnmarshalOptions wraps the input parameters for `UnmarshalXxx` functions.
    33  type UnmarshalOptions struct {
    34  	// Version is the version of the received response.
    35  	Version string
    36  	// Resources are the xDS resources resources in the received response.
    37  	Resources []*anypb.Any
    38  	// Logger is the prefix logger to be used during unmarshaling.
    39  	Logger *grpclog.PrefixLogger
    40  	// UpdateValidator is a post unmarshal validation check provided by the
    41  	// upper layer.
    42  	UpdateValidator UpdateValidatorFunc
    43  }
    44  
    45  // processAllResources unmarshals and validates the resources, populates the
    46  // provided ret (a map), and returns metadata and error.
    47  //
    48  // After this function, the ret map will be populated with both valid and
    49  // invalid updates. Invalid resources will have an entry with the key as the
    50  // resource name, value as an empty update.
    51  //
    52  // The type of the resource is determined by the type of ret. E.g.
    53  // map[string]ListenerUpdate means this is for LDS.
    54  func processAllResources(opts *UnmarshalOptions, ret interface{}) (UpdateMetadata, error) {
    55  	timestamp := time.Now()
    56  	md := UpdateMetadata{
    57  		Version:   opts.Version,
    58  		Timestamp: timestamp,
    59  	}
    60  	var topLevelErrors []error
    61  	perResourceErrors := make(map[string]error)
    62  
    63  	for _, r := range opts.Resources {
    64  		switch ret2 := ret.(type) {
    65  		case map[string]ListenerUpdateErrTuple:
    66  			name, update, err := unmarshalListenerResource(r, opts.UpdateValidator, opts.Logger)
    67  			name = ParseName(name).String()
    68  			if err == nil {
    69  				ret2[name] = ListenerUpdateErrTuple{Update: update}
    70  				continue
    71  			}
    72  			if name == "" {
    73  				topLevelErrors = append(topLevelErrors, err)
    74  				continue
    75  			}
    76  			perResourceErrors[name] = err
    77  			// Add place holder in the map so we know this resource name was in
    78  			// the response.
    79  			ret2[name] = ListenerUpdateErrTuple{Err: err}
    80  		case map[string]RouteConfigUpdateErrTuple:
    81  			name, update, err := unmarshalRouteConfigResource(r, opts.Logger)
    82  			name = ParseName(name).String()
    83  			if err == nil {
    84  				ret2[name] = RouteConfigUpdateErrTuple{Update: update}
    85  				continue
    86  			}
    87  			if name == "" {
    88  				topLevelErrors = append(topLevelErrors, err)
    89  				continue
    90  			}
    91  			perResourceErrors[name] = err
    92  			// Add place holder in the map so we know this resource name was in
    93  			// the response.
    94  			ret2[name] = RouteConfigUpdateErrTuple{Err: err}
    95  		case map[string]ClusterUpdateErrTuple:
    96  			name, update, err := unmarshalClusterResource(r, opts.UpdateValidator, opts.Logger)
    97  			name = ParseName(name).String()
    98  			if err == nil {
    99  				ret2[name] = ClusterUpdateErrTuple{Update: update}
   100  				continue
   101  			}
   102  			if name == "" {
   103  				topLevelErrors = append(topLevelErrors, err)
   104  				continue
   105  			}
   106  			perResourceErrors[name] = err
   107  			// Add place holder in the map so we know this resource name was in
   108  			// the response.
   109  			ret2[name] = ClusterUpdateErrTuple{Err: err}
   110  		case map[string]EndpointsUpdateErrTuple:
   111  			name, update, err := unmarshalEndpointsResource(r, opts.Logger)
   112  			name = ParseName(name).String()
   113  			if err == nil {
   114  				ret2[name] = EndpointsUpdateErrTuple{Update: update}
   115  				continue
   116  			}
   117  			if name == "" {
   118  				topLevelErrors = append(topLevelErrors, err)
   119  				continue
   120  			}
   121  			perResourceErrors[name] = err
   122  			// Add place holder in the map so we know this resource name was in
   123  			// the response.
   124  			ret2[name] = EndpointsUpdateErrTuple{Err: err}
   125  		}
   126  	}
   127  
   128  	if len(topLevelErrors) == 0 && len(perResourceErrors) == 0 {
   129  		md.Status = ServiceStatusACKed
   130  		return md, nil
   131  	}
   132  
   133  	var typeStr string
   134  	switch ret.(type) {
   135  	case map[string]ListenerUpdate:
   136  		typeStr = "LDS"
   137  	case map[string]RouteConfigUpdate:
   138  		typeStr = "RDS"
   139  	case map[string]ClusterUpdate:
   140  		typeStr = "CDS"
   141  	case map[string]EndpointsUpdate:
   142  		typeStr = "EDS"
   143  	}
   144  
   145  	md.Status = ServiceStatusNACKed
   146  	errRet := combineErrors(typeStr, topLevelErrors, perResourceErrors)
   147  	md.ErrState = &UpdateErrorMetadata{
   148  		Version:   opts.Version,
   149  		Err:       errRet,
   150  		Timestamp: timestamp,
   151  	}
   152  	return md, errRet
   153  }
   154  
   155  func combineErrors(rType string, topLevelErrors []error, perResourceErrors map[string]error) error {
   156  	var errStrB strings.Builder
   157  	errStrB.WriteString(fmt.Sprintf("error parsing %q response: ", rType))
   158  	if len(topLevelErrors) > 0 {
   159  		errStrB.WriteString("top level errors: ")
   160  		for i, err := range topLevelErrors {
   161  			if i != 0 {
   162  				errStrB.WriteString(";\n")
   163  			}
   164  			errStrB.WriteString(err.Error())
   165  		}
   166  	}
   167  	if len(perResourceErrors) > 0 {
   168  		var i int
   169  		for name, err := range perResourceErrors {
   170  			if i != 0 {
   171  				errStrB.WriteString(";\n")
   172  			}
   173  			i++
   174  			errStrB.WriteString(fmt.Sprintf("resource %q: %v", name, err.Error()))
   175  		}
   176  	}
   177  	return errors.New(errStrB.String())
   178  }