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  }