github.com/kubewharf/katalyst-core@v0.5.3/pkg/util/service-discovery/pod_single_port_sd.go (about)

     1  /*
     2  Copyright 2022 The Katalyst Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package service_discovery
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"net"
    23  	"sync"
    24  	"time"
    25  
    26  	v1 "k8s.io/api/core/v1"
    27  	"k8s.io/apimachinery/pkg/api/errors"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/labels"
    30  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    31  	"k8s.io/apimachinery/pkg/util/wait"
    32  	"k8s.io/client-go/informers"
    33  	corelisters "k8s.io/client-go/listers/core/v1"
    34  	"k8s.io/client-go/tools/cache"
    35  	"k8s.io/klog/v2"
    36  
    37  	katalystbase "github.com/kubewharf/katalyst-core/cmd/base"
    38  	"github.com/kubewharf/katalyst-core/pkg/config/generic"
    39  	"github.com/kubewharf/katalyst-core/pkg/util/native"
    40  )
    41  
    42  func init() { RegisterSDManagerInitializers(ServiceDiscoveryPodSinglePort, NewPodSinglePortSDManager) }
    43  
    44  const ServiceDiscoveryPodSinglePort = "pod-single-port"
    45  
    46  const (
    47  	defaultPodReSyncPeriod = time.Hour * 24
    48  	defaultPodSyncPeriod   = time.Second * 3
    49  )
    50  
    51  type podSinglePortSDManager struct {
    52  	sync.RWMutex
    53  	endpoints map[string]string
    54  
    55  	portName   string
    56  	ctx        context.Context
    57  	podLister  corelisters.PodLister
    58  	syncedFunc cache.InformerSynced
    59  }
    60  
    61  func NewPodSinglePortSDManager(ctx context.Context, agentCtx *katalystbase.GenericContext,
    62  	conf *generic.ServiceDiscoveryConf,
    63  ) (ServiceDiscoveryManager, error) {
    64  	klog.Infof("%v sd manager enabled with pod selector: %v", ServiceDiscoveryPodSinglePort, conf.PodLister.String())
    65  	podFactory := informers.NewSharedInformerFactoryWithOptions(agentCtx.Client.KubeClient, defaultPodReSyncPeriod,
    66  		informers.WithTweakListOptions(func(options *metav1.ListOptions) {
    67  			options.LabelSelector = conf.PodLister.String()
    68  		}))
    69  	podInformer := podFactory.Core().V1().Pods()
    70  
    71  	m := &podSinglePortSDManager{
    72  		portName:   conf.PodSinglePortSDConf.PortName,
    73  		ctx:        ctx,
    74  		endpoints:  make(map[string]string),
    75  		podLister:  podInformer.Lister(),
    76  		syncedFunc: podInformer.Informer().HasSynced,
    77  	}
    78  
    79  	podInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{
    80  		FilterFunc: func(obj interface{}) bool {
    81  			switch t := obj.(type) {
    82  			case *v1.Pod:
    83  				return native.PodIsReady(t)
    84  			case cache.DeletedFinalStateUnknown:
    85  				if pod, ok := t.Obj.(*v1.Pod); ok {
    86  					return native.PodIsReady(pod)
    87  				}
    88  				utilruntime.HandleError(fmt.Errorf("unable to convert object %T to *v1.Pod", obj))
    89  				return false
    90  			default:
    91  				utilruntime.HandleError(fmt.Errorf("unable to handle object: %T", obj))
    92  				return false
    93  			}
    94  		},
    95  		Handler: cache.ResourceEventHandlerFuncs{
    96  			AddFunc:    m.addPod,
    97  			UpdateFunc: m.updatePod,
    98  			DeleteFunc: m.deletePod,
    99  		},
   100  	})
   101  
   102  	podFactory.Start(ctx.Done())
   103  
   104  	return m, nil
   105  }
   106  
   107  func (m *podSinglePortSDManager) Name() string { return ServiceDiscoveryPodSinglePort }
   108  
   109  func (m *podSinglePortSDManager) GetEndpoints() ([]string, error) {
   110  	m.RLock()
   111  	defer m.RUnlock()
   112  
   113  	endpoints := make([]string, 0, len(m.endpoints))
   114  	for _, ep := range m.endpoints {
   115  		endpoints = append(endpoints, ep)
   116  	}
   117  
   118  	return endpoints, nil
   119  }
   120  
   121  func (m *podSinglePortSDManager) Run() error {
   122  	if !cache.WaitForCacheSync(m.ctx.Done(), m.syncedFunc) {
   123  		return fmt.Errorf("unable to sync caches for podSinglePortSDManager")
   124  	}
   125  
   126  	go wait.Until(m.sync, defaultPodSyncPeriod, m.ctx.Done())
   127  	return nil
   128  }
   129  
   130  func (m *podSinglePortSDManager) addPod(obj interface{}) {
   131  	pod, ok := obj.(*v1.Pod)
   132  	if !ok {
   133  		klog.ErrorS(nil, "Cannot convert to *v1.Pod", "obj", obj)
   134  		return
   135  	}
   136  
   137  	klog.V(6).Infof("add pod %v", pod.Name)
   138  	m.addEndpoint(pod)
   139  }
   140  
   141  func (m *podSinglePortSDManager) updatePod(_, newObj interface{}) {
   142  	newPod, ok := newObj.(*v1.Pod)
   143  	if !ok {
   144  		klog.ErrorS(nil, "Cannot convert to *v1.Pod", "obj", newObj)
   145  		return
   146  	}
   147  
   148  	klog.V(6).Infof("update pod %v", newPod.Name)
   149  	m.addEndpoint(newPod)
   150  }
   151  
   152  func (m *podSinglePortSDManager) deletePod(obj interface{}) {
   153  	pod, ok := obj.(*v1.Pod)
   154  	if !ok {
   155  		klog.ErrorS(nil, "Cannot convert to *v1.Pod", "obj", obj)
   156  		return
   157  	}
   158  
   159  	klog.V(6).Infof("delete pod %v", pod.Name)
   160  	m.removeEndpoint(pod)
   161  }
   162  
   163  func (m *podSinglePortSDManager) removeEndpoint(pod *v1.Pod) {
   164  	m.Lock()
   165  	defer m.Unlock()
   166  
   167  	key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(pod)
   168  	if err != nil {
   169  		klog.Errorf("couldn't get key for pod %#v: %v", pod, err)
   170  		return
   171  	}
   172  
   173  	delete(m.endpoints, key)
   174  }
   175  
   176  func (m *podSinglePortSDManager) addEndpoint(pod *v1.Pod) {
   177  	m.Lock()
   178  	defer m.Unlock()
   179  
   180  	m.addEndpointWithoutLock(pod)
   181  }
   182  
   183  func (m *podSinglePortSDManager) addEndpointWithoutLock(pod *v1.Pod) {
   184  	key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(pod)
   185  	if err != nil {
   186  		klog.Errorf("couldn't get key for pod %#v: %v", pod, err)
   187  		return
   188  	} else if _, exist := m.endpoints[key]; exist {
   189  		return
   190  	}
   191  
   192  	endpoint, err := m.getPodEndpoint(pod)
   193  	if err != nil {
   194  		klog.ErrorS(err, "get new endpoint failed", "pod", pod.Name)
   195  		return
   196  	}
   197  
   198  	klog.Infof("add endpoint %s for pod %v", endpoint, key)
   199  
   200  	m.endpoints[key] = endpoint
   201  }
   202  
   203  func (m *podSinglePortSDManager) getPodEndpoint(pod *v1.Pod) (string, error) {
   204  	port, ok := native.ParseHostPortForPod(pod, m.portName)
   205  	if !ok {
   206  		return "", fmt.Errorf("pod has invalid valid port:%v", m.portName)
   207  	}
   208  
   209  	hostIPs, ok := native.GetPodHostIPs(pod)
   210  	if !ok {
   211  		return "", fmt.Errorf("pod has invalid valid host-ip")
   212  	}
   213  
   214  	for _, hostIP := range hostIPs {
   215  		url := fmt.Sprintf("[%s]:%d", hostIP, port)
   216  		if conn, err := net.DialTimeout("tcp", url, time.Second*5); err == nil {
   217  			if conn != nil {
   218  				_ = conn.Close()
   219  			}
   220  			return url, nil
   221  		} else {
   222  			klog.Errorf("pod %v dial %v failed: %v", pod.Name, url, err)
   223  		}
   224  	}
   225  
   226  	return "", fmt.Errorf("invalid endpoint exits")
   227  }
   228  
   229  func (m *podSinglePortSDManager) sync() {
   230  	m.Lock()
   231  	defer m.Unlock()
   232  
   233  	for key := range m.endpoints {
   234  		namespace, name, err := cache.SplitMetaNamespaceKey(key)
   235  		if err != nil {
   236  			klog.Errorf("failed to split namespace and name from key %s", key)
   237  			continue
   238  		}
   239  
   240  		if _, err := m.podLister.Pods(namespace).Get(name); err != nil {
   241  			if errors.IsNotFound(err) {
   242  				delete(m.endpoints, key)
   243  			} else {
   244  				klog.Errorf("failed to get pod %s: %s", key, err)
   245  			}
   246  		}
   247  	}
   248  
   249  	pods, err := m.podLister.List(labels.Everything())
   250  	if err != nil {
   251  		klog.Errorf("failed to list pods: %v", err)
   252  		return
   253  	}
   254  	for _, pod := range pods {
   255  		m.addEndpointWithoutLock(pod)
   256  	}
   257  }