github.com/kuoss/venti@v0.2.20/pkg/service/discovery/kubernetes/kubernetes.go (about)

     1  package kubernetes
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  
     8  	"github.com/kuoss/venti/pkg/model"
     9  	v1 "k8s.io/api/core/v1"
    10  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    11  	"k8s.io/client-go/kubernetes"
    12  	"k8s.io/client-go/rest"
    13  )
    14  
    15  type k8sService struct {
    16  	client kubernetes.Interface
    17  }
    18  
    19  func NewK8sService() (*k8sService, error) {
    20  	clusterCfg, err := rest.InClusterConfig()
    21  	if err != nil {
    22  		return nil, fmt.Errorf("cannot InClusterConfig: %w", err)
    23  	}
    24  	clientset, err := kubernetes.NewForConfig(clusterCfg)
    25  	if err != nil {
    26  		return nil, fmt.Errorf("cannot NewForConfig: %w", err)
    27  	}
    28  	return &k8sService{client: clientset}, nil
    29  }
    30  
    31  func (s *k8sService) Do(discovery model.Discovery) ([]model.Datasource, error) {
    32  
    33  	services, err := s.client.CoreV1().Services("").List(context.Background(), metav1.ListOptions{})
    34  	if err != nil {
    35  		return nil, fmt.Errorf("cannot ListServices: %w", err)
    36  	}
    37  	return s.getDatasourcesFromServices(services.Items, discovery), nil
    38  }
    39  
    40  func (s *k8sService) getDatasourcesFromServices(services []v1.Service, discovery model.Discovery) []model.Datasource {
    41  	var datasources []model.Datasource
    42  
    43  	for _, service := range services {
    44  		datasourceType := getDatasourceTypeByConfig(service, discovery)
    45  
    46  		// the service is not a datasource
    47  		if datasourceType == model.DatasourceTypeNone {
    48  			continue
    49  		}
    50  
    51  		// recognize as a main datasource by namespace
    52  		isMain := false
    53  		if service.Namespace == discovery.MainNamespace {
    54  			isMain = true
    55  		}
    56  
    57  		// get port number of datasource from k8s service
    58  		portNumber, err := getPortNumberFromService(service)
    59  		if err != nil {
    60  			log.Printf("extract port number from service failed. %s", err)
    61  			continue
    62  		}
    63  		// append to datasources
    64  		datasources = append(datasources, model.Datasource{
    65  			Name:         fmt.Sprintf("%s.%s", service.Name, service.Namespace),
    66  			Type:         datasourceType,
    67  			URL:          fmt.Sprintf("http://%s.%s:%d", service.Name, service.Namespace, portNumber),
    68  			IsDiscovered: true,
    69  			IsMain:       isMain,
    70  		})
    71  	}
    72  	return datasources
    73  }
    74  
    75  // getDatasourceTypeByConfig return DatasourceType.
    76  // 1. If configured within config.Discovery.ByNamePrometheus or config.Discovery.ByNameLethe return if service has matched name.
    77  // 2. If configured within config.Discovery.AnnotationKey matched with service's annotation key and also value is
    78  // one of promethe or lethe.
    79  func getDatasourceTypeByConfig(service v1.Service, cfg model.Discovery) model.DatasourceType {
    80  
    81  	// recognize as a datasource by name 'prometheus'
    82  	if cfg.ByNamePrometheus && service.Name == "prometheus" {
    83  		return model.DatasourceTypePrometheus
    84  	}
    85  	// recognize as a datasource by name 'lethe'
    86  	if cfg.ByNameLethe && service.Name == "lethe" {
    87  		return model.DatasourceTypeLethe
    88  	}
    89  
    90  	// recognize as a datasource by annotation of k8s service
    91  	for key, value := range service.Annotations {
    92  		if key != cfg.AnnotationKey {
    93  			continue
    94  		}
    95  		if value == string(model.DatasourceTypePrometheus) {
    96  			return model.DatasourceTypePrometheus
    97  		}
    98  		if value == string(model.DatasourceTypeLethe) {
    99  			return model.DatasourceTypeLethe
   100  		}
   101  	}
   102  
   103  	return model.DatasourceTypeNone
   104  }
   105  
   106  // return port number within "http" named port. if not exist return service's first port number
   107  func getPortNumberFromService(service v1.Service) (int32, error) {
   108  	if len(service.Spec.Ports) < 1 {
   109  		return 0, fmt.Errorf("service %s/%s have any port", service.Namespace, service.Name)
   110  	}
   111  
   112  	for _, port := range service.Spec.Ports {
   113  		if port.Name == "http" {
   114  			return port.Port, nil
   115  		}
   116  	}
   117  	return service.Spec.Ports[0].Port, nil
   118  }