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