github.com/polarismesh/polaris@v1.17.8/apiserver/xdsserverv3/resource/node.go (about) 1 /** 2 * Tencent is pleased to support the open source community by making Polaris available. 3 * 4 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. 5 * 6 * Licensed under the BSD 3-Clause License (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * https://opensource.org/licenses/BSD-3-Clause 11 * 12 * Unless required by applicable law or agreed to in writing, software distributed 13 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 14 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 15 * specific language governing permissions and limitations under the License. 16 */ 17 18 package resource 19 20 import ( 21 "encoding/json" 22 "fmt" 23 "regexp" 24 "strconv" 25 "strings" 26 "sync" 27 28 core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 29 structpb "github.com/golang/protobuf/ptypes/struct" 30 "go.uber.org/zap" 31 ) 32 33 type RunType string 34 35 var ( 36 // RunTypeGateway xds node run type is gateway 37 RunTypeGateway RunType = "gateway" 38 // RunTypeSidecar xds node run type is sidecar 39 RunTypeSidecar RunType = "sidecar" 40 ) 41 42 const ( 43 sep = "~" 44 // GatewayNamespaceName xds metadata key when node is run in gateway mode 45 GatewayNamespaceName = "gateway.polarismesh.cn/serviceNamespace" 46 // GatewayNamespaceName xds metadata key when node is run in gateway mode 47 GatewayServiceName = "gateway.polarismesh.cn/serviceName" 48 // OldGatewayNamespaceName xds metadata key when node is run in gateway mode 49 OldGatewayNamespaceName = "gateway_namespace" 50 // OldGatewayServiceName xds metadata key when node is run in gateway mode 51 OldGatewayServiceName = "gateway_service" 52 // SidecarServiceName xds metadata key when node is run in sidecar mode 53 SidecarServiceName = "sidecar.polarismesh.cn/serviceName" 54 // SidecarNamespaceName xds metadata key when node is run in sidecar mode 55 SidecarNamespaceName = "sidecar.polarismesh.cn/serviceNamespace" 56 // SidecarBindPort xds metadata key when node is run in sidecar mode 57 SidecarBindPort = "sidecar.polarismesh.cn/bindPorts" 58 ) 59 60 func NewXDSNodeManager() *XDSNodeManager { 61 return &XDSNodeManager{ 62 nodes: map[string]*XDSClient{}, 63 streamTonodes: map[int64]*XDSClient{}, 64 sidecarNodes: map[string]*XDSClient{}, 65 gatewayNodes: map[string]*XDSClient{}, 66 } 67 } 68 69 type XDSNodeManager struct { 70 lock sync.RWMutex 71 nodes map[string]*XDSClient 72 streamTonodes map[int64]*XDSClient 73 // sidecarNodes The XDS client is the node list of the SIDECAR run mode 74 sidecarNodes map[string]*XDSClient 75 // gatewayNodes The XDS client is the node list of the Gateway run mode 76 gatewayNodes map[string]*XDSClient 77 } 78 79 func (x *XDSNodeManager) AddNodeIfAbsent(streamId int64, node *core.Node) { 80 if node == nil { 81 return 82 } 83 x.lock.Lock() 84 defer x.lock.Unlock() 85 86 p := parseNodeProxy(node) 87 88 if _, ok := x.streamTonodes[streamId]; !ok { 89 x.streamTonodes[streamId] = p 90 } 91 if _, ok := x.nodes[node.Id]; !ok { 92 x.nodes[node.Id] = p 93 } 94 95 switch p.RunType { 96 case RunTypeGateway: 97 if _, ok := x.gatewayNodes[node.Id]; !ok { 98 x.gatewayNodes[node.Id] = p 99 log.Info("[XDS][Node][V3] add gateway xds node", zap.Int64("stream", streamId), 100 zap.String("info", p.String())) 101 } 102 default: 103 if _, ok := x.sidecarNodes[node.Id]; !ok { 104 x.sidecarNodes[node.Id] = p 105 log.Info("[XDS][Node][V3] add sidecar xds node", zap.Int64("stream", streamId), 106 zap.String("info", p.String())) 107 } 108 } 109 } 110 111 func (x *XDSNodeManager) DelNode(streamId int64) { 112 x.lock.Lock() 113 defer x.lock.Unlock() 114 115 if p, ok := x.streamTonodes[streamId]; ok { 116 delete(x.nodes, p.Node.Id) 117 log.Info("[XDS][Node][V3] remove xds node", zap.Int64("stream", streamId), 118 zap.String("info", p.String())) 119 } 120 delete(x.streamTonodes, streamId) 121 } 122 123 func (x *XDSNodeManager) GetNodeByStreamID(streamId int64) *XDSClient { 124 x.lock.RLock() 125 defer x.lock.RUnlock() 126 127 return x.streamTonodes[streamId] 128 } 129 130 func (x *XDSNodeManager) GetNode(id string) *XDSClient { 131 x.lock.RLock() 132 defer x.lock.RUnlock() 133 134 return x.nodes[id] 135 } 136 137 func (x *XDSNodeManager) ListGatewayNodes() []*XDSClient { 138 x.lock.RLock() 139 defer x.lock.RUnlock() 140 141 ret := make([]*XDSClient, 0, len(x.gatewayNodes)) 142 for i := range x.gatewayNodes { 143 ret = append(ret, x.gatewayNodes[i]) 144 } 145 return ret 146 } 147 148 func (x *XDSNodeManager) ListSidecarNodes() []*XDSClient { 149 x.lock.RLock() 150 defer x.lock.RUnlock() 151 152 ret := make([]*XDSClient, 0, len(x.sidecarNodes)) 153 for i := range x.sidecarNodes { 154 ret = append(ret, x.sidecarNodes[i]) 155 } 156 return ret 157 } 158 159 // ID id 的格式是 ${sidecar|gateway}~namespace/uuid~hostIp 160 // case 1: envoy 为 sidecar 模式时,则 NodeID 的格式为以下两种 161 // 162 // eg 1. namespace/uuid~hostIp 163 // eg 2. sidecar~namespace/uuid-hostIp 164 // eg 3. envoy_node_id="${NAMESPACE}/${INSTANCE_IP}~${POD_NAME}" 165 // 166 // case 2: envoy 为 gateway 模式时,则 NodeID 的格式为: gateway~namespace/uuid~hostIp 167 func (PolarisNodeHash) ID(node *core.Node) string { 168 if node == nil { 169 return "" 170 } 171 172 runType, ns, _, _ := ParseNodeID(node.Id) 173 if node.Metadata == nil || len(node.Metadata.Fields) == 0 { 174 return ns 175 } 176 177 // Gateway 类型直接按照 gateway_service 以及 gateway_namespace 纬度 178 if runType != string(RunTypeSidecar) { 179 gatewayNamespace := node.Metadata.Fields[GatewayNamespaceName].GetStringValue() 180 gatewayService := node.Metadata.Fields[GatewayServiceName].GetStringValue() 181 // 兼容老的 envoy gateway metadata 参数设置 182 if gatewayNamespace == "" { 183 gatewayNamespace = node.Metadata.Fields[OldGatewayNamespaceName].GetStringValue() 184 } 185 if gatewayService == "" { 186 gatewayService = node.Metadata.Fields[OldGatewayServiceName].GetStringValue() 187 } 188 if gatewayNamespace == "" { 189 gatewayNamespace = ns 190 } 191 return strings.Join([]string{runType, gatewayNamespace, gatewayService}, "/") 192 } 193 // 兼容老版本注入的 envoy, 默认获取 snapshot resource 粒度为 namespace 级别, 只能下发 OUTBOUND 规则 194 ret := ns 195 196 // 判断是否存在 sidecar_namespace 以及 sidecar_service 197 if node.Metadata != nil && node.Metadata.Fields != nil { 198 sidecarNamespace := node.Metadata.Fields[SidecarNamespaceName].GetStringValue() 199 sidecarService := node.Metadata.Fields[SidecarServiceName].GetStringValue() 200 // 如果存在, 则表示是由新版本 controller 注入的 envoy, 可以下发 INBOUND 规则 201 if sidecarNamespace != "" && sidecarService != "" { 202 ret = runType + "/" + sidecarNamespace + "/" + sidecarService 203 } 204 205 // 在判断是否设置了 TLS 相关参数 206 tlsMode := node.Metadata.Fields[TLSModeTag].GetStringValue() 207 if tlsMode == string(TLSModePermissive) || tlsMode == string(TLSModeStrict) { 208 return ret + "/" + tlsMode 209 } 210 } 211 return ret 212 } 213 214 // PolarisNodeHash 存放 hash 方法 215 type PolarisNodeHash struct { 216 NodeMgr *XDSNodeManager 217 } 218 219 // node id 的格式是: 220 // 1. namespace/uuid~hostIp 221 var nodeIDFormat = regexp.MustCompile(`^((\S+)~(\S+)|(\S+))\/([^~]+)~([^~]+)$`) 222 223 func ParseNodeID(nodeID string) (runType, polarisNamespace, uuid, hostIP string) { 224 groups := nodeIDFormat.FindStringSubmatch(nodeID) 225 if len(groups) == 0 { 226 // invalid node format 227 return 228 } 229 prefixInfo := groups[1] 230 if strings.Contains(prefixInfo, sep) { 231 runType = groups[2] 232 polarisNamespace = groups[3] 233 } else { 234 // 默认为 sidecar 模式 235 runType = "sidecar" 236 polarisNamespace = groups[1] 237 } 238 uuid = groups[5] 239 hostIP = groups[6] 240 return 241 } 242 243 // XDSClient 客户端代码结构体 244 type XDSClient struct { 245 RunType RunType 246 User string 247 Namespace string 248 IPAddr string 249 PodIP string 250 Metadata map[string]string 251 Version string 252 Node *core.Node 253 TLSMode TLSMode 254 } 255 256 func (n *XDSClient) String() string { 257 return fmt.Sprintf("nodeid=%s|type=%v|user=%s|addr=%s|version=%s|tls=%s|meta=%s", n.Node.GetId(), 258 n.RunType, n.User, n.IPAddr, n.Version, n.TLSMode, toJSON(n.Metadata)) 259 } 260 261 func (n *XDSClient) IsGateway() bool { 262 service := n.Metadata[GatewayServiceName] 263 namespace := n.Metadata[GatewayNamespaceName] 264 oldSvc := n.Metadata[OldGatewayServiceName] 265 oldSvcNamespace := n.Metadata[OldGatewayNamespaceName] 266 hasNew := service != "" && namespace != "" 267 hasOld := oldSvc != "" && oldSvcNamespace != "" 268 return n.RunType == RunTypeGateway && (hasNew || hasOld) 269 } 270 271 // GetSelfService 获取 envoy 对应的 service 信息 272 func (n *XDSClient) GetSelfService() string { 273 if n.IsGateway() { 274 val, ok := n.Metadata[GatewayServiceName] 275 if ok { 276 return val 277 } 278 return n.Metadata[OldGatewayServiceName] 279 } 280 return n.Metadata[SidecarServiceName] 281 } 282 283 // GetSelfNamespace 获取 envoy 对应的 namespace 信息 284 func (n *XDSClient) GetSelfNamespace() string { 285 if n.IsGateway() { 286 val, ok := n.Metadata[GatewayNamespaceName] 287 if ok { 288 return val 289 } 290 val, ok = n.Metadata[OldGatewayNamespaceName] 291 if ok { 292 return val 293 } 294 return n.Namespace 295 } 296 val, ok := n.Metadata[SidecarNamespaceName] 297 if ok { 298 return val 299 } 300 return n.Namespace 301 } 302 303 func parseNodeProxy(node *core.Node) *XDSClient { 304 runType, polarisNamespace, _, hostIP := ParseNodeID(node.Id) 305 proxy := &XDSClient{ 306 IPAddr: hostIP, 307 PodIP: hostIP, 308 RunType: RunType(runType), 309 Namespace: polarisNamespace, 310 Node: node, 311 TLSMode: TLSModeNone, 312 } 313 314 if node.Metadata != nil { 315 fieldVal, ok := node.Metadata.Fields[TLSModeTag] 316 if ok { 317 tlsMode := fieldVal.GetStringValue() 318 if tlsMode == string(TLSModePermissive) { 319 proxy.TLSMode = TLSModePermissive 320 } 321 if tlsMode == string(TLSModeStrict) { 322 proxy.TLSMode = TLSModeStrict 323 } 324 } 325 } 326 327 proxy.Metadata = parseMetadata(node.GetMetadata()) 328 return proxy 329 } 330 331 func parseMetadata(metaValues *structpb.Struct) map[string]string { 332 fields := metaValues.GetFields() 333 if len(fields) == 0 { 334 return nil 335 } 336 var values = make(map[string]string, len(fields)) 337 for fieldKey, fieldValue := range fields { 338 switch fieldValue.GetKind().(type) { 339 case *structpb.Value_StringValue: 340 values[fieldKey] = fieldValue.GetStringValue() 341 case *structpb.Value_NumberValue: 342 values[fieldKey] = strconv.FormatInt(int64(fieldValue.GetNumberValue()), 10) 343 case *structpb.Value_BoolValue: 344 values[fieldKey] = strconv.FormatBool(fieldValue.GetBoolValue()) 345 } 346 } 347 return values 348 } 349 350 func toJSON(m map[string]string) string { 351 if m == nil { 352 return "{}" 353 } 354 355 ba, err := json.Marshal(m) 356 if err != nil { 357 return "{}" 358 } 359 return string(ba) 360 }