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 }