github.com/kubewharf/katalyst-core@v0.5.3/pkg/util/service-discovery/service_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  	"time"
    24  
    25  	v1 "k8s.io/api/core/v1"
    26  	corelisters "k8s.io/client-go/listers/core/v1"
    27  	"k8s.io/client-go/tools/cache"
    28  	"k8s.io/klog/v2"
    29  
    30  	katalystbase "github.com/kubewharf/katalyst-core/cmd/base"
    31  	"github.com/kubewharf/katalyst-core/pkg/config/generic"
    32  )
    33  
    34  func init() {
    35  	RegisterSDManagerInitializers(ServiceDiscoveryServiceSinglePort, NewServiceSinglePortSDManager)
    36  }
    37  
    38  const ServiceDiscoveryServiceSinglePort = "service-single-port"
    39  
    40  type serviceSinglePortSDManager struct {
    41  	ctx context.Context
    42  
    43  	namespace string
    44  	name      string
    45  	portName  string
    46  
    47  	svcLister  corelisters.ServiceLister
    48  	epLister   corelisters.EndpointsLister
    49  	syncedFunc []cache.InformerSynced
    50  }
    51  
    52  func NewServiceSinglePortSDManager(ctx context.Context, agentCtx *katalystbase.GenericContext,
    53  	conf *generic.ServiceDiscoveryConf,
    54  ) (ServiceDiscoveryManager, error) {
    55  	klog.Infof("%v sd manager enabled with conf: %+v", ServiceDiscoveryServiceSinglePort, conf.ServiceSinglePortSDConf)
    56  	svcInformer := agentCtx.KubeInformerFactory.Core().V1().Services()
    57  	epInformer := agentCtx.KubeInformerFactory.Core().V1().Endpoints()
    58  
    59  	s := &serviceSinglePortSDManager{
    60  		ctx: ctx,
    61  
    62  		namespace: conf.ServiceSinglePortSDConf.Namespace,
    63  		name:      conf.ServiceSinglePortSDConf.Name,
    64  		portName:  conf.ServiceSinglePortSDConf.PortName,
    65  
    66  		epLister:  epInformer.Lister(),
    67  		svcLister: svcInformer.Lister(),
    68  		syncedFunc: []cache.InformerSynced{
    69  			svcInformer.Informer().HasSynced,
    70  			epInformer.Informer().HasSynced,
    71  		},
    72  	}
    73  	return s, nil
    74  }
    75  
    76  func (s *serviceSinglePortSDManager) Name() string { return ServiceDiscoveryServiceSinglePort }
    77  
    78  func (s *serviceSinglePortSDManager) GetEndpoints() ([]string, error) {
    79  	svc, err := s.svcLister.Services(s.namespace).Get(s.name)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	eps, err := s.epLister.Endpoints(s.namespace).Get(svc.Name)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  	if len(eps.Subsets) == 0 {
    89  		return nil, fmt.Errorf("no endpoints available for service %q", svc.Name)
    90  	}
    91  
    92  	var res []string
    93  	for i := range eps.Subsets {
    94  		if urls, err := s.getValidAddresses(&eps.Subsets[i]); err != nil {
    95  			klog.Errorf("subsets %v get valid address err: %v", eps.Subsets[i].String(), err)
    96  		} else {
    97  			res = append(res, urls...)
    98  		}
    99  	}
   100  	return res, nil
   101  }
   102  
   103  func (s *serviceSinglePortSDManager) Run() error {
   104  	if !cache.WaitForCacheSync(s.ctx.Done(), s.syncedFunc...) {
   105  		return fmt.Errorf("unable to sync caches for podSinglePortSDManager")
   106  	}
   107  	return nil
   108  }
   109  
   110  func (s *serviceSinglePortSDManager) getValidAddresses(ss *v1.EndpointSubset) ([]string, error) {
   111  	results := make([]string, 0)
   112  	for i := range ss.Ports {
   113  		if ss.Ports[i].Name == s.portName {
   114  			for _, address := range ss.Addresses {
   115  				url := fmt.Sprintf("[%s]:%d", address.IP, ss.Ports[i].Port)
   116  				if conn, err := net.DialTimeout("tcp", url, time.Second*5); err == nil {
   117  					if conn != nil {
   118  						_ = conn.Close()
   119  					}
   120  					results = append(results, url)
   121  				} else {
   122  					klog.Errorf("dial %v failed: %v", url, err)
   123  				}
   124  			}
   125  		}
   126  	}
   127  
   128  	if len(results) == 0 {
   129  		return results, fmt.Errorf("no valid endpoint exists")
   130  	} else {
   131  		return results, nil
   132  	}
   133  }